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