有没有办法处理未知的泛型类型?

时间:2012-03-01 11:13:01

标签: c# generics dynamic

我有这段代码

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
  }

但问题是,如何设置生产者的Consumer属性?生产者实例是我的一个对象,我无法将其转换为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;

1 个答案:

答案 0 :(得分:6)

不幸的是,如果不重构您提供的代码,就无法在没有更多反射的情况下解决问题(正如您所做的那样)。但是,您可以在设置使用者属性之前使用反射,如果它使您更具可读性。

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;
}

您是否给了&gt; 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>);
  }
}

我不喜欢这个解决方案,但我认为最佳解决方案需要更多关于您当前代码库的信息 - 否则我会给出一个与您已有的解决方案不同的答案。