And*_*eas 0 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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我有一个控制器,它接受任何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
}
Run Code Online (Sandbox Code Playgroud)
但问题是,如何设置生产者的Consumer属性?生产者实例是我的一个对象,我无法将其转换为一个IProducer<T>,因为我不能像使用T一样使用T.
我可以用反射来做,producerInstance.GetType().GetProperty("Consumer").SetValue(producerInstance, consumerInstance, null);但我想知道是否有另一种方式?
有趣的是,这在运行时失败了:
MyClass consumerInstance;
dynamic test = producerInstance;
test.Consumer = consumerInstance;
Run Code Online (Sandbox Code Playgroud)
它抱怨说,consumerInstance的类型与属性的类型不兼容.
编辑:动态示例仅在consumerInstance也是动态时才起作用,例如:
dynamic testC = consumerInstance;
dynamic testP = producerInstance;
testP.Consumer = testC;
Run Code Online (Sandbox Code Playgroud)
不幸的是,如果不重构你提供的代码,你就无法在没有更多反思的情况下解决问题(正如你所做的那样).但是,您可以在设置使用者属性之前使用反射,如果它使您更具可读性.
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;
}
Run Code Online (Sandbox Code Playgroud)
你给MyType提供> 1 IConsumable并且只改变泛型类型参数吗?我假设你是因为你得到了这些接口的列表.我不知道你从哪里得到你的制作人,但不使用反射的唯一方法就是远离它.您可以考虑强制每个"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>);
}
}
Run Code Online (Sandbox Code Playgroud)
我不喜欢这个解决方案,但我觉得最佳解决方案需要更多关于你当前代码库的信息 - 否则我会给出一个与你已经拥有的内容有太多不同的答案.