2012-03-01 83 views
0

我有這樣的代碼有沒有辦法處理未知的泛型類型?

public interface IConsumable<T> { 
    void Consume(T item); 
} 

public interface IProducer<T> { 
    IConsumable<T> Consumer { get; set; } 
    void Produce(); 
} 

public class MyClass : MyType, 
    IConsumable<ISpecifcItem> 
{ 
    public void Consume(ISpecificItem item) { ... } 
} 

public class MySpecificItemProducer 
    : IProducer<ISpecificItem> { 
    public IConsumable<ISpecificItem> Consumer { get; set; } 
    public void Produce() { 
    ISpecificItem myItem = new MyVerySpecificItem(); 
    Consumer.Consume(myItem); 
    } 
} 

然後我有一個控制器,它採取任何MyType,發現各類IConsumable<>它實現並得到泛型類型參數的類型。有了這個類型列表,它可以發現實現IProducer<TParam>的所有生產者。這並不難:

var consumerTypes = 
    myType.GetType().GetInterfaces() 
    .Where(x => x.IsGenericType) 
    .Where(x => x.GetGenericTypeDefinition() == 
     typeof(IConsumable<>)); 

    if (consumerTypes.Any()) { 
    var instanceTypes = consumerTypes 
     .Select(x => x.GetGenericArguments().First()) 
     .Select(x => typeof(IProducer<>).MakeGenericType(x)); 
    // for each of those types discover classes where 
    // it assignable from 
    // and instantiate the class using the Activator 
    } 

但問題是,我該如何設置生產者的消費者屬性?生產者實例對我來說是一個對象,我不能將它轉換爲IProducer<T>,因爲我不能像變量那樣使用T.

我可以用反射producerInstance.GetType().GetProperty("Consumer").SetValue(producerInstance, consumerInstance, null);但我想知道是否有另一種方式嗎?

有趣的是,這並沒有在運行時:

MyClass consumerInstance; 
dynamic test = producerInstance; 
test.Consumer = consumerInstance; 

這抱怨那consumerInstance的類型是不兼容的屬性的類型。

編輯:動態例子只是工作時,consumerInstance也是一個動態的,例如:

dynamic testC = consumerInstance; 
dynamic testP = producerInstance; 
testP.Consumer = testC; 
+0

您的動態代碼適合我。 – Ani 2012-03-01 12:09:59

+0

嗯好吧,我只是試過一次,也許別的東西。 – Andreas 2012-03-01 13:22:53

+0

我跟進了這個動態的例子,發現它在我讓受讓人也是動態的時候起作用。 – Andreas 2012-03-11 21:03:03

回答

5

不幸的是,沒有重構你所提供的代碼,你不能沒有更多的思考解決的問題(如你所做的) 。但是,在設置consumer屬性之前,您可以使用反射,如果它使它更易於讀取。

var method = GetType().GetMethod("Process"); 
var genericType = interfaceType.GetGenericArguments().First(); 
var invocable = method.MakeGenericMethod(genericType); 

invocable.Invoke(this, new object[] { producer, consumer }); 

public void Process<T>(IProducer<T> producer, IConsumable<T> consumer) 
{ 
    producer.Consumer = consumer; 
} 

你是否給了> 1 IConsumable給MyType,只是改變泛型類型的參數?我假設你是因爲你得到了這些接口的列表。我不知道你從哪裏得到你的製作人,但是不使用反思的唯一方法就是遠離它。你可以考慮迫使每個'MyType'提供一個'設置'生產者列表的方法(MyType內部會知道它所有的消費類型)。根據你拉的(內部的MyType或外部)的生產商,你可能必須做到以下幾點:

public interface IProducer { } 

public interface IProducer<T> : IProducer 
{ 
    IConsumable<T> Consumer { get; set; } 
    void Produce(); 
} 

public interface IConsumableProvider 
{ 
    void SetupProducers(params IProducer[] producers); 
} 

public class MyType : 
    IConsumable<int>, 
    IConsumable<double>, 
    IConsumableProvider 
{ 
    public void Consume(int item) 
    { 
    throw new NotImplementedException(); 
    } 

    public void Consume(double item) 
    { 
    throw new NotImplementedException(); 
    } 

    public void SetupProducers(params IProducer[] producers) 
    { 
    (producers[0] as IProducer<int>).Consumer = (this as IConsumable<int>); 
    (producers[1] as IProducer<double>).Consumer = (this as IConsumable<double>); 
    } 
} 

我不是愛上了解決方案,但我覺得最佳的解決方案將需要更多的信息關於你當前的代碼基礎 - 否則我會給出一個與你已有的分歧。

+0

謝謝,說實話,我之前有一個無反射的解決方案,但那需要非通用接口頂部和一些鑄件。它還在界面中添加了一些我不喜歡的方法。 – Andreas 2012-03-01 13:17:38

+0

我喜歡你的解決方案,因爲它將字符串引用和引用的方法放在同一個類中。按照你的說法,設置雖然有點複雜,但這讓我感覺更安全。當某人重構屬性名稱時,更改將自動轉發。 – Andreas 2012-03-01 13:20:38

+0

很酷,我很高興這有幫助!我能得到upvote嗎? :) – payo 2012-03-01 17:53:56

相關問題