如何在字典中放置实现某个通用接口的所有类型?

时间:2010-05-24 12:12:41

标签: c# generics reflection

鉴于特定界面ITarget<T>和特定类型myType,如果T实施myType,您可以通过以下方式确定ITarget<T>。 (此代码段取自an earlier question的答案。)

foreach (var i in myType.GetInterfaces ())
    if (i.IsGenericType
        && i.GetGenericTypeDefinition() == typeof(ITarget<>))
        return i.GetGenericArguments ()[0] ;

但是,这只会检查类型myType。如何创建所有此类型参数的词典,其中键为T且值为myType?我认为它看起来像这样:

var searchTarget = typeof(ITarget<>);
var dict = Assembly.GetExecutingAssembly().[???]
             .Where(t => t.IsGenericType
                    && t.GetGenericTypeDefinition() == searchTarget)
             .[???];

空白是什么?

1 个答案:

答案 0 :(得分:6)

var searchTarget = typeof(ITarget<>);

var dict = Assembly.GetExecutingAssembly()
    .GetTypes()
    .SelectMany(t => t.GetInterfaces()
                      .Where(i => i.IsGenericType
                          && (i.GetGenericTypeDefinition() == searchTarget)
                          && !i.ContainsGenericParameters),
                (t, i) => new { Key = i.GetGenericArguments()[0], Value = t })
    .ToDictionary(x => x.Key, x => x.Value);

请注意,如果您有多个类实现ITarget<>并使用相同的泛型类型参数 - 例如class Foo : ITarget<string>class Bar : ITarget<string> - 则ToDictionary调用将失败,ArgumentException抱怨你不能两次添加相同的密钥。

如果您确实需要一对多映射,那么您可以使用几个选项。

  1. 使用ToLookup而非ToDictionary生成Lookup<K,V>

    var dict = Assembly.GetExecutingAssembly()
        .GetTypes()
        .SelectMany(/* ... */)
        .ToLookup(x => x.Key, x => x.Value);
    
  2. 如果您更喜欢使用类似Dictionary<K,List<V>>的内容,那么您可以这样做:

    var dict = Assembly.GetExecutingAssembly()
        .GetTypes()
        .SelectMany(/* ... */)
        .GroupBy(x => x.Key, x => x.Value)
        .ToDictionary(g => g.Key, g => g.ToList());