2012-03-01 85 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

相关问题