Есть ли способ получить тип унаследованного универсального класса, используя отражение? - PullRequest
0 голосов
/ 08 апреля 2019

У меня есть это как моя подпись класса:

public class LocationRule : IRule<Location>

Я пытаюсь использовать reflection для доступа к типу реализации IRule.

Используя этот код

Assembly.GetTypes()
  .Where(type => typeof(IRule<Location>).IsAssignableFrom(type) 
    && !type.IsInterface)

Я могу получить классы, которые наследуют эту конкретную реализацию IRule.Однако нет способа динамически вставить тип в этот expression, например, так.

Assembly.GetTypes()
  .Where(type => typeof(IRule<typeof(AnotherClass)>).IsAssignableFrom(type) 
    && !type.IsInterface)

Можно ли найти все классы, которые реализуют IRule<>, а затем выяснить все реализованные типы?

public class LocationRule : IRule<Location>
public class NameRule : IRule<Name>

Есть ли способ овладеть каждым из этих типов?Имя и местоположение, поэтому я могу разместить их в Dictionary<TypeOfIRuleGeneric, TypeOfRuleClass>, т. Е. Ключ = местоположение, значение = LocationRule?

Спасибо.

Ответы [ 2 ]

3 голосов
/ 08 апреля 2019

Вы ищете какую-то "общность" между всеми IRule<T> типами. Вы можете определить базовый интерфейс, как предлагает dcg, но это не приведет ко второй части вашего вопроса, где вы хотите извлечь параметры универсального типа и вставить их в словарь.

Существует вещь, называемая определением универсального типа , которая представляет «урезанный» универсальный тип со всеми удаленными параметрами универсального типа. Вместо этого вы можете использовать это как «общность».

typeof(IRule<Location>).GetGenericTypeDefinition() // MyApp.IRule`1[T]

Но C # позволяет вам на самом деле использовать неопределенный универсальный тип в typeof, чтобы дать вам то же самое более кратко:

typeof(IRule<>) // compiles! Also gives MyApp.IRule`1[T]

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

Я создал вспомогательный метод, который получает все общие интерфейсы, реализованные с помощью типа:

public static IEnumerable<Type> GetGenericInterfaces(this Type type)
{
    return type.GetInterfaces().Where(t => t.IsGenericType);
}

Вот еще один метод, который сообщает мне, могу ли я создать тип из заданного определения универсального типа:

public static bool IsConstructableFrom(this Type type, Type genericTypeDefinition)
{
    return type.IsConstructedGenericType &&
        (type.GetGenericTypeDefinition() == genericTypeDefinition);
}

Теперь ваш запрос будет:

var types = assembly.GetTypes().Where(type =>
        !type.IsInterface &&
        type.GetGenericInterfaces().Any(generic => generic.IsConstructableFrom(typeof(IRule<>))))
    .ToArray();

Но, в конечном итоге, вам нужно Dictionary<TypeOfIRuleGeneric, TypeOfRuleClass>, где ключ - это параметр общего типа IRule<>, а значение - это класс, который его реализует. Я предполагаю, что у вас будет не более одного класса, который реализует IRule<T> для определенного T (верно ли это предположение?). Я также предполагаю, что каждый класс будет реализовывать не более одного IRule<T> (верно ли это предположение?).

Есть много способов сделать эту часть. Я придумал это:

var dict = assembly.GetTypes()
    .Where(type => !type.IsInterface)
    .Select(type => new
    {
        TypeOfRuleClass = type,
        IRuleInterface = type
            .GetGenericInterfaces().FirstOrDefault(generic => generic.IsConstructableFrom(typeof(IRule<>)))
    })
    .Where(t => t.IRuleInterface != null)
    .ToDictionary(t => t.TypeOfRuleClass, t => t.IRuleInterface.GetGenericArguments()[0]);
  • Первый Where отфильтровывает интерфейсы из возможных типов
  • Select преобразует каждый тип-кандидат в кортеж (TypeOfRuleClass, IRuleInterface), где IRuleInterface - это первый (и единственный, по предположению) совпадающий тип IRule<T>, реализованный TypeOfRuleClass; или null, если тип не реализует IRule<T>
  • Второй Where отфильтровывает типы кандидатов, которые не реализуют IRule<T>
  • ToDictionary создает нужный словарь, ключом которого является TypeOfRuleClass, а значением является параметр универсального типа.
0 голосов
/ 08 апреля 2019

Если вы хотите получить все типы, которые реализуют определенный интерфейс, вы можете сделать это следующим образом:

public interface IMyInterface { }

public class MyClass: IMyInterface { }

//...
var types = Assembly.GetExecutingAssembly()
    .GetTypes()
    .Where(t => !t.IsInterface && t.GetInterfaces().Contains(typeof(IMyInterface)))
    .ToList();

Здесь types будет заполнено MyClass.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...