Как отразить интерфейсный тип <t>во время выполнения - PullRequest
0 голосов
/ 29 мая 2018

У меня есть несколько точек данных и связанный с ними процессор данных для каждой.

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 = ??

}

Как решить для «X», чтобы я могсоздать экземпляр и обработать ввод?

Update # 1 Объединяя ответы Славы и Лаки снизу, я получаю следующее, но выдает исключение - «Объект не соответствует типу цели».хотя в отладчике все выглядит нормально.Можно ли приводить как IDataPointProcessor <> и вызывать метод интерфейса чисто, то есть: 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 Чтобы сохранить движение, мне сложнозакодировал тип Age_Input для проверки работоспособности.Чего мне не хватает, чтобы динамически вызывать жестко закодированный бит?

Я должен иметь возможность привести экземпляр к 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 голосов
/ 01 июня 2018

Как обычно, если вы можете избежать Reflection, вам, как правило, лучше.Я обменял незначительный запах кода на гораздо более простое решение.

По сути, я вернулся к основам и использовал тупые интерфейсы, а также вспомогательный метод на входе, который возвращал заполненный экземпляр соответствующего процессора.

Теперь мой большой цикл отражения заменен следующим:

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

Запах кода такой - не проблема

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

Полный код ниже

#region Interfaces
public interface IDataPointProcessor
{
    IDataPointOutput GetOutput();
}

public interface IDataPointInput
{
    IDataPointProcessor GetProcessor();
}

public interface IDataPointOutput
{
    List<string> DebugStrings { get; set; }
} 
#endregion

#region Base Classes
public abstract class DataPointProcessorBase : IDataPointProcessor
{
    public abstract IDataPointOutput GetOutput();
}

public abstract class DataPointInputBase : IDataPointInput
{
    public abstract IDataPointProcessor GetProcessor();
}

public abstract class DataPointOutputBase : IDataPointOutput
{
    public List<string> DebugStrings { get; set; }

    public DataPointOutputBase()
    {
        DebugStrings = new List<string>();
    }
} 
#endregion

public class Age_Output : DataPointOutputBase
{
}

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

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

public class Age_Processor : DataPointProcessorBase
{
    public Age_Input Input { get; set; }
    public Age_Processor(Age_Input input)
    {
        Input = input;
    }

    public override IDataPointOutput GetOutput()
    {
        Age_Output output = new Age_Output();


        if (Input.AgeExact > 30)
        {
            output.DebugStrings.Add("Getting old");
        }
        else
        {
            output.DebugStrings.Add("Still young");
        }
        return output;
    }
}

public class DecisionEngine
{
    public void GetDecisions()
    {
        List<IDataPointInput> input = new List<IDataPointInput>();
        input.Add(new Age_Input { AgeExact = 44 });
        foreach (IDataPointInput item in input)
        {
            IDataPointProcessor processor = item.GetProcessor();
            IDataPointOutput output = processor.GetOutput();

        }
    }
}
0 голосов
/ 30 мая 2018

Во-первых, вы должны сделать ковариантным свой интерфейс следующим образом.

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

Вы должны получить типы, которые реализованы IDataPointProcessor<>, затем вы должны создать экземпляр полученного типа и вызвать метод generic.тип.

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() });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...