给定特定的接口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)
             .[???];


什么空白?

最佳答案

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抱怨您可以这样做的情况。 t两次添加相同的密钥。

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


使用ToLookup而不是ToDictionary生成Lookup<K,V>

var dict = Assembly.GetExecutingAssembly()
    .GetTypes()
    .SelectMany(/* ... */)
    .ToLookup(x => x.Key, x => x.Value);

如果您喜欢使用Dictionary<K,List<V>>之类的方法,则可以这样做:

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

10-07 22:36