如何在运行时

时间:2018-05-29 20:55:35

标签: c# generics reflection covariance

我有多个数据点和相关的数据处理器。

public interface IDataPointProcessor<T> where T : DataPointInputBase
{
    DataPointOutputBase GetOutput(T input);
}

我从文件加载数据点列表,并希望使用其单个关联处理器处理它们。

foreach (DataPointInputBase item in input.DataPoints)
{
    //assuming item coming in is of type 'X' how do I get correct processor
    var type = typeof(IDataPointProcessor<X>);
    var types = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(s => s.GetTypes())
                .Where(p => type.IsAssignableFrom(p) && !p.IsAbstract);

    IDataPointProcessor<X> matchedType = ??

}

我如何解决&#39; X&#39;所以我可以实例化它并处理输入?

更新#1 结合来自Slava和Lucky的下面的答案我得到以下内容,但它引发了一个异常 - &#39;对象与目标类型不匹配。&#39;即使它在调试器中似乎都匹配好了。是否可以转换为IDataPointProcessor&lt;&gt;并干净地调用接口方法,即:instance.GetOutput(item);

foreach (DataPointInputBase item in input.DataPoints)
{
    Type typeGenArg = item.GetType();

    Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);

    Type type = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(x => x.GetTypes())
        .Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
        .FirstOrDefault();

    Type genericType = typeof(IDataPointProcessor<>);

    Type dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));

    var method = dependedGenericType.GetMethod("GetOutput");
    var instance = Activator.CreateInstance(type);
    //currently throws:System.Reflection.TargetException: 'Object does not match target type.'
    var result = method.Invoke(instance, new object[] { item });
    //Ideally I want to do this and avoid the magic strings etc
    //var temp_output = instance.GetOutput(item);
}

更新#2 为了让事情顺利进行,我已经对类型&#39; Age_Input&#39;进行了硬编码。验证事情是否有效。我错过了什么动态调用硬编码位?

我应该能够将实例转换为IDataPointProcessor<IDataPointInput>并在界面上调用GetOutput()

foreach (IDataPointInput item in input.DataPoints)
{
    Type typeGenArg = item.GetType();

    Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);

    Type type = AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(x => x.GetTypes())
        .Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
        .FirstOrDefault();

    Type genericType = typeof(IDataPointProcessor<>);

    Type dependedGenericType = genericType.MakeGenericType(typeof(IDataPointInput));

    var instance = Activator.CreateInstance(type);

    if (instance is IDataPointProcessor<Age_Input>)//hard-coded
    {
        var processor = instance as IDataPointProcessor<Age_Input>;
        Age_Input temp = item as Age_Input;
        var result = processor.GetOutput(temp);
    }
    if (instance is DataPointProcessorBase<DataPointInputBase>)
    {
        //false
    }
    if (instance is IDataPointProcessor<DataPointInputBase>)
    {
        //false
    }
    if (instance is IDataPointProcessor<IDataPointInput>)
    {
        //false - shouldn't this work?
    }
}

Age_Input是一个普通的类,继承自哑基类和空接口

public class Age_Input : DataPointInputBase, IDataPointInput
{
    public int AgeExact { get; set; }
} 

public class DataPointInputBase : IDataPointInput
{
}
public interface IDataPointInput
{
}

处理器类同样简单

 public abstract class DataPointProcessorBase<T> : IDataPointProcessor<T> where T : IDataPointInput, new()
    {
        //public abstract DataPointOutputBase GetOutput(DataPointInputBase input);
        public abstract DataPointOutputBase GetOutput(T input);
    }

    public interface IDataPointInput
    {
    }

    public interface IDataPointProcessor<IDataPointInput> 
    {
        DataPointOutputBase GetOutput(IDataPointInput input);
    }

2 个答案:

答案 0 :(得分:0)

首先,你应该像这样对你的界面进行协变。

public interface IDataPointProcessor<in T> where T : DataPointInputBase
{
    DataPointOutputBase GetOutput(T input);
}

您应该检索由IDataPointProcessor<>实现的类型,然后您应该创建检索类型的实例并调用泛型类型的方法。

Type genericType = typeof(IDataPointProcessor<>);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => genericType.IsAssignableFrom(p) && !p.IsAbstract).ToList();


var dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));
var method = dependedGenericType.GetMethod("GetOutput");
var instance = Activator.CreateInstance(types[0]);
method.Invoke(instance, new object[] { new DataPointInputBase() });

答案 1 :(得分:0)

通常情况下,如果你可以避免反思,你通常会变得更好。我交换了一点点代码味道,以获得更简单的解决方案。

基本上我回到了基础并使用了哑接口,并在输入上有一个帮助方法,它返回了相应处理器的引用实例。

现在我的大反射循环被替换为:

_id (for COLUMN_ID)             
total_size(for COLUMN_TOTAL_SIZE_BYTES)

代码味道是这个 - 不是问题

foreach (IDataPointInput item in input)
{
    IDataPointProcessor processor = item.GetProcessor();
    IDataPointOutput output = processor.GetOutput();
}

以下完整代码

public override IDataPointProcessor GetProcessor()
{
    return new Age_Processor(this);
}