2012-04-13 63 views
5

我一直在解決一個問題幾個小時,現在我認爲我很接近。我正在開發一款應用程序,我們可以使用50-100種類型來執行相同的操作。因此,而不是創建50-100班,我試圖使它通用的,這是我所:在運行時創建具有通用接口的通用類型

這是基類:

public class RavenWriterBase<T> : IRavenWriter<T> where T : class, IDataEntity 

這是接口:

public interface IRavenWriter<T> 
{ 
    int ExecutionIntervalInSeconds { get; } 
    void Execute(object stateInfo); 
    void Initialize(int executionIntervalInSeconds, Expression<Func<T, DateTime>> timeOrderByFunc); 
} 

這是我如何使用它:

private static void StartWriters() 
{ 
    Assembly assembly = typeof(IDataEntity).Assembly; 
    List<IDataEntity> dataEntities = ReflectionUtility.GetObjectsForAnInterface<IDataEntity>(assembly); 

    foreach (IDataEntity dataEntity in dataEntities) 
    { 
     Type dataEntityType = dataEntity.GetType(); 
     Type ravenWriterType = typeof(RavenWriterBase<>).MakeGenericType(dataEntityType); 

     Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

     // This is where I'm stuck. How do I activate this as RavenWriterBase<T>? 
     var ravenWriter = Activator.CreateInstance(ravenWriterType); 

     //ravenWriter.Initialize(60, func); // I can't do this until I cast. 

     // More functionality here (not part of this issue) 
    } 
} 

我被困在這條線上面:

var ravenWriter = Activator.CreateInstance(ravenWriterType); 

這是我的問題:
我如何使用,作爲RavenWriterBase或IRavenWriter?喜歡的東西:

ravenWriter.Initialize(60, func); 

我認爲它需要是這樣的,但我需要指定一個類型IRavenWriter <>,我不知道它:

var ravenWriter = Activator.CreateInstance(ravenWriterType) as IRavenWriter<>; 

如果我將鼠標懸停在ravenWriter,我成功有我的對象:

enter image description here

但現在我需要能夠在一個通用的方式來使用它。我怎樣才能做到這一點?

更新:

我只是想使用動態關鍵字,而這個工程:

dynamic ravenWriter = Activator.CreateInstance(ravenWriterType); 
ravenWriter.Initialize(60); 

我被騙了一下,因爲我意識到,Func鍵是爲每個IDataEntity相同,因此這不需要作爲參數傳遞給Initialize()。不過,至少現在我可以通過來調用Initialize()。但是現在Func是一樣的,我也不需要通用接口。

+2

聽起來像不正當使用的仿製藥。當你發現自己需要根據不同的類型運行不同的方法,但只有在運行時才知道類型,你想要使用多態。什麼是泛型獲得你?您是否可以按照原樣運行方法而不使用泛型,即使用IDataEntity? – mellamokb 2012-04-13 15:41:32

+1

我同意mellamokb。然而,如果你設置泛型,爲什麼不創建一個包裝類,你可以實例化非genarically。包裝應該有一個返回RavenWriterBase 的實例的方法,具體取決於作爲參數傳入的類型。然後,實例化包裝器並以請求的類型作爲參數調用該方法。這將需要一個大的switch語句,但至少不需要200個獨立的類。 – dcow 2012-04-13 15:46:32

+2

您是否創建其他表達式不使用IDataEntity for T?或者T總是一個IDataEntity?如果T總是IDataEntity,爲什麼要使用泛型? – 2012-04-13 15:47:10

回答

2

我的解決辦法是:

  • 創建的IRavenWriter
  • 非一般的接口,IRavenWriter
  • IRavenWriter<T>繼承保持ExecuteExecutionIntervalInSecondsIRavenWriter
  • IRavenWriterFunc<DateTime>和使用在你的作家
  • M奧雅納InitializeIRavenWriter<T>
  • 使用工廠根據類型和表達式初始化函數功能:

例如:

public class MyDateTime 
{ 
    public DateTime This { get; set; } 
} 

public static Func<DateTime> GetFunk<T>(Expression<Func<T, DateTime>> timeOrderByFunc, T t) 
{ 
    return() => timeOrderByFunc.Compile()(t); 
} 

你使用:

GetFunk<MyDateTime>(x => x.This, new MyDateTime(){This = DateTime.Now}); 
+0

我不確定我關注。這是怎麼讓我使用ravenWriter作爲RavenWriterBase <>或IRavenWriter <>? – 2012-04-13 16:27:12

+0

@BobHorn它沒有。你不能在那裏瞭解RavenWriterBase <>'。相反,你需要在那裏使用'IRavenWriterBase'(非泛型)。該類型的編譯時知識不存在,因此您無法投射。 – Aliostad 2012-04-13 16:34:54

+0

謝謝。我剛剛編輯我的問題,以顯示如何使用動態調用Initialize()。我認爲這應該工作。 – 2012-04-13 16:36:57

1

這不是真的很難將運行時Type轉換爲編譯時的泛型類型參數。與dataEntityType

 

interface IRawenWriterFactory 
{ 
    object Create(); 
} 

class RawenWriterFactory<T> : IRawenWriterFactory 
{ 
    public object Create() 
    { 
    Expression<Func<IDataEntity, DateTime>> func = x => x.CicReadTime; 

    var ravenWriter = new RavenWriterBase<T>(); 
    ravenWriter.Initialize(60, func); 

    return ravenWriter; 
    } 
} 
 

現在只需創建RawenWriterFactory就像你已經創建ravenWriter,並通過非通用IRavenWriterFactory接口使用它:只要引進新的界面,用於創建/初始化你的對象。

但是,如果您要更改設計,可能會有更簡單的解決方案。例如。如果將Initialize方法轉換爲構造函數,則可以將func作爲Activator.CreateInstance參數傳遞,並且根本不需要使用通用接口進行初始化。