Как поместить все типы, соответствующие определенному интерфейсу C #, в IDictionary? - PullRequest
1 голос
/ 19 мая 2010

У меня есть несколько классов в одном интерфейсе, все в одной сборке, все в соответствии с одним и тем же универсальным интерфейсом:

public class AppleFactory : IFactory<Apple> { ... }
public class BananaFactory : IFactory<Banana> { ... }
// ...

Можно с уверенностью предположить, что если у нас есть IFactory<T> для определенного T, то это единственный такой тип. (То есть, нет двух вещей, которые реализуют IFactory<Apple>.)

Я бы хотел использовать отражение, чтобы получить все эти типы, а затем сохранить их все в IDictionary, где ключом является typeof(T), а значением является соответствующий IFactory<T>. Я полагаю, что в конечном итоге мы получим что-то вроде этого:

_map = new Dictionary<Type, object>();

foreach(Type t in [...]) {
  object factoryForType = System.Reflection.[???](t);
  _map[t] = factoryForType;
}

Какой лучший способ сделать это? У меня проблемы с просмотром, как бы я это сделал с System.Reflection интерфейсами.

Ответы [ 4 ]

2 голосов
/ 19 мая 2010

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

public static class TypeExtensions
{
    public static bool IsGenericTypeOf(this Type t, Type genericDefinition)
    {
        return t.IsGenericType && genericDefinition.IsGenericType && t.GetGenericTypeDefinition() == genericDefinition.GetGenericTypeDefinition();
    }
}

Затем вы можете использовать метод Type.GetGenericArguments find "T".

Такой, что вы получите что-то вроде:

var allTypes = ...; // Maybe: Assembly.GetExecutingAssembly().GetTypes()
var dictionary = allTypes.Where(t => t.IsGenericTypeOf(typeof(IFactory<>))).ToDictionary(t => t.GetGenericArguments()[0], t => t);
1 голос
/ 19 мая 2010
private readonly Dictionary<Type, object> _map = Assembly.GetExecutingAssembly()
    .GetTypes()
    .SelectMany(t => t.GetInterfaces()
                      .Where(i => i.IsGenericType
                          && (i.GetGenericTypeDefinition() == typeof(IFactory<>))
                          && !i.ContainsGenericParameters),
                (t, i) => new
                    {
                        KeyType = i.GetGenericArguments()[0],
                        Factory = Activator.CreateInstance(t)
                    })
    .ToDictionary(x => x.KeyType, x => x.Factory);

// ...

public IFactory<T> GetFactory<T>()
{
    object factory;
    if (_map.TryGetValue(typeof(T), out factory))
        return (IFactory<T>)factory;

    // no factory found so return null (or throw an exception if you prefer)
    return null;
}
1 голос
/ 19 мая 2010
        foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
        {
            bool isIFactory = (from i in type.GetInterfaces()
                               where i.IsGenericType &&
                                     i.GetGenericTypeDefinition() == typeof(IFactory<>) &&
                                     i.IsAssignableFrom(i.GetGenericArguments()[0])
                                     // this one could be also changed to i.GetGenericArguments()[0] == type
                                     // however, it'll generate an anonymous class, which will show in the outer foreach
                               select i).Count() == 1;
        }
0 голосов
/ 19 мая 2010

Проблема будет соответствовать всем IFactory, потому что вы должны указать T. Самое простое решение - создать другой интерфейс IFactory и сделать IFactory<T> его общей версией. Затем вы можете сопоставить все IFactory и затем построить словарь.

Assembly.GetExecutingAssembly().GetTypes()
    .Where(t => t is IFactory)
    .ToDictionary(k => k.GetGenericArguments()[0],v => v);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...