获取实现接口的所有类型

jua*_*uan 524 c# reflection optimization lambda c#-3.0

使用反射,如何使用最少的代码获得使用C#3.0/.NET 3.5实现接口的所有类型,并最大限度地减少迭代?

这就是我想要重写的内容:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
Run Code Online (Sandbox Code Playgroud)

Dar*_*opp 773

我的将是这个在c#3.0 :)

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));
Run Code Online (Sandbox Code Playgroud)

基本上,迭代次数最少的是:

loop assemblies  
 loop types  
  see if implemented.
Run Code Online (Sandbox Code Playgroud)

  • 请注意,列表还可能包含接口本身.将最后一行更改为`.Where(p => type.IsAssignableFrom(p)&&!p.IsInterface);`将其过滤掉(或`p.IsClass`). (184认同)
  • 注意:这个答案是错误的!这会检查"分配兼容性",而不是接口是否实现.例如`List <string>`没有实现`IEnumerable <object>`但是这个方法将在.Net 4.0中返回true,因为协方差确实是错误的.[正确答案在这里](http://stackoverflow.com/a/12602220/2530848) (37认同)
  • 你是绝对正确的darren,我知道这是一个老线程,我只是注册我的评论只是为了让未来的用户意识到存在这样的问题.不要得罪你.并且问题标题说OP是否要求*获取实现接口的所有类型*此代码不会这样做.但毫无疑问,几乎所有的情况**都有效**.我说的也有角落案件.只是要意识到它; (23认同)
  • @SriramSakthivel首先关闭,未指定通用值.其次,这个问题早于协方差.第三,你假设协变回报不是他们想要的东西. (19认同)
  • 还需要确保该类不是抽象的=>`.Where(p => type.IsAssignableFrom(p)&& p.IsClass &&!p.IsAbstract` (9认同)
  • 我想补充一点,检查类型是否不是抽象会排除接口和抽象类,因此 `.Where(p =&gt; type.IsAssignableFrom(p) &amp;&amp; !p.IsAbstract)` 应该足够了。 (3认同)
  • 此外,没有必要声明 `var type = typeof(IMyInterface)`,因为 `typeof(IMyInterface)` 在编译时被评估,并且不会在 `Where` 语句中一遍又一遍地重新评估。 (2认同)
  • @Jonesopolis 询问者没有指定他们是否想要实例化该类。抽象类仍然可以实现接口,并且可能是您想要寻找的,具体取决于您正在做什么。 (2认同)

小智 60

这对我有用.它循环遍历类并检查它们是否来自myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
Run Code Online (Sandbox Code Playgroud)

  • 您假设程序集位于主可执行文件中.不是一个额外的项目.您也通过一堆迭代不必要地迭代.让框架完成繁重的工作会更好.然后在找到时向下过滤.如果相关,请更新您的答案.包括List <T>推理.var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).其中(mytype => typeof(myInterface).IsAssignableFrom(mytype)&& mytype.GetInterfaces().Contains(typeof(myInterface) )); foreach(项目中的var项)Console.Log(item.Name); (4认同)

Jud*_*ngo 55

要查找实现IFoo接口的程序集中的所有类型:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;
Run Code Online (Sandbox Code Playgroud)

请注意,Ryan Rinaldi的建议不正确.它将返回0种类型.你不能写

where type is IFoo
Run Code Online (Sandbox Code Playgroud)

因为type是System.Type实例,并且永远不会是IFoo类型.而是检查是否可以从类型中分配IFoo.这将获得您的预期结果.

此外,Adam Wright的建议,目前被标记为答案,也是不正确的,并且出于同样的原因.在运行时,您将看到0种类型返回,因为所有System.Type实例都不是IFoo实现者.


ris*_*ism 54

我很欣赏这是一个非常古老的问题,但我想我会为未来的用户添加另一个答案,因为所有日期答案都使用某种形式Assembly.GetTypes.

虽然GetTypes()确实会返回所有类型,但它并不一定意味着你可以激活它们,因此可能会抛出一个ReflectionTypeLoadException.

无法激活类型的典型示例是返回的类型derived来自base但是basederived与调用程序集未引用的程序集不同的程序集中定义.

所以说我们有:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA
Run Code Online (Sandbox Code Playgroud)

如果我们在ClassC其中,AssemblyC那么按照接受的答案做一些事情:

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));
Run Code Online (Sandbox Code Playgroud)

然后它会抛出一个ReflectionTypeLoadException.

这是因为没有参考AssemblyA ,AssemblyC你将无法:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);
Run Code Online (Sandbox Code Playgroud)

换句话说ClassB,不能加载,这是对GetTypes的调用检查和抛出的东西.

因此,为了安全地限定可加载类型的结果集,然后根据这个Phil Haacked文章获取程序集中的所有类型Jon Skeet代码,您将改为执行以下操作:

public static class TypeLoaderExtensions {
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
        if (assembly == null) throw new ArgumentNullException("assembly");
        try {
            return assembly.GetTypes();
        } catch (ReflectionTypeLoadException e) {
            return e.Types.Where(t => t != null);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
Run Code Online (Sandbox Code Playgroud)

  • 这有助于我处理一个超级奇怪的问题,在我的测试项目中GetTypes会失败,只在我们的CI环境中.GetLoadableTypes是此解决方案的修复程序.该错误在本地环境中不可重现,它是:System.Reflection.ReflectionTypeLoadException:无法加载一个或多个请求的类型.检索LoaderExceptions属性以获取更多信息.更具体地说,它抱怨有一种没有具体实施的类型,它发生在单元测试项目中.谢谢你! (3认同)
  • 万一它对其他人有帮助:这个解决方案对我有用,但是我必须对其进行修改以从列表中删除接口类型。我想为所有它们都激活`CreateInstance`,并且在尝试创建实际接口时引发了异常(当我认为实际接口不适合该解决方案时,这让我感到困惑)。所以我将代码更改为`GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t =&gt;!(t.Equals(interfaceType)))。ToList();`。 (3认同)
  • 这个答案应该标记为解决方案,它今天救了我的屁股,因为就像@Lari Tuomisto 所说的,在本地环境中我们无法重新生成类似的错误 (2认同)

hil*_*tuk 20

其他答案在这里使用IsAssignableFrom.您也可以使用FindInterfacesSystem命名空间,如所描述这里.

下面是一个示例,它检查当前正在执行的程序集文件夹中的所有程序集,查找实现某个接口的类(为清楚起见,避免使用LINQ).

static void Main() {
    const string qualifiedInterfaceName = "Interfaces.IMyInterface";
    var interfaceFilter = new TypeFilter(InterfaceFilter);
    var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    var di = new DirectoryInfo(path);
    foreach (var file in di.GetFiles("*.dll")) {
        try {
            var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
            foreach (var type in nextAssembly.GetTypes()) {
                var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
                if (myInterfaces.Length > 0) {
                    // This class implements the interface
                }
            }
        } catch (BadImageFormatException) {
            // Not a .net assembly  - ignore
        }
    }
}

public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
    return typeObj.ToString() == criteriaObj.ToString();
}
Run Code Online (Sandbox Code Playgroud)

如果要匹配多个接口,可以设置接口列表.


ang*_*son 18

循环遍历所有已加载的程序集,遍历所有类型,并检查它们是否实现了接口.

就像是:

Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
    foreach (Type t in asm.GetTypes()) {
        if (ti.IsAssignableFrom(t)) {
            // here's your type in t
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


rvn*_*ord 9

我在这里看到了很多过于复杂的答案,人们总是告诉我我倾向于使事情变得过于复杂。同样以IsAssignableFrom解决OP问题为目的的方法也是错误的!

这是我的示例,它从应用程序域中选择所有程序集,然后获取所有可用类型的平面列表并检查每个类型的接口列表是否匹配:

public static IEnumerable<Type> GetImplementingTypes(this Type itype) 
    => AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes())
           .Where(t => t.GetInterfaces().Contains(itype));
Run Code Online (Sandbox Code Playgroud)


小智 8

这对我有用(如果你希望你可以在查找中排除系统类型):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
Run Code Online (Sandbox Code Playgroud)


Ant*_*REL 6

其他答案不适用于通用接口

这样做,只需将 typeof(ISomeInterface) 替换为 typeof (T)。

List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
            .Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
            .Select(x => x.Name).ToList();
Run Code Online (Sandbox Code Playgroud)

所以与

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
Run Code Online (Sandbox Code Playgroud)

我们得到了所有的程序集

!x.IsInterface && !x.IsAbstract
Run Code Online (Sandbox Code Playgroud)

用于排除接口和抽象接口和

.Select(x => x.Name).ToList();
Run Code Online (Sandbox Code Playgroud)

将它们列在列表中。


Chi*_*nke 6

迄今为止发布的所有答案都考虑了太少或太多的程序集。您只需检查引用包含该接口的程序集的程序集。这可以最大限度地减少不必要运行的静态构造函数的数量,并节省大量时间,并在第三方程序集的情况下节省可能出现的意外副作用。

public static class ReflectionUtils
{
    public static bool DoesTypeSupportInterface(Type type, Type inter)
    {
        if (inter.IsAssignableFrom(type))
            return true;
        if (type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == inter))
            return true;
        return false;
    }

    public static IEnumerable<Assembly> GetReferencingAssemblies(Assembly assembly)
    {
        return AppDomain
            .CurrentDomain
            .GetAssemblies().Where(asm => asm.GetReferencedAssemblies().Any(asmName => AssemblyName.ReferenceMatchesDefinition(asmName, assembly.GetName())));
    }

    public static IEnumerable<Type> TypesImplementingInterface(Type desiredType)
    {
        var assembliesToSearch = new Assembly[] { desiredType.Assembly }
            .Concat(GetReferencingAssemblies(desiredType.Assembly));
        return assembliesToSearch.SelectMany(assembly => assembly.GetTypes())
            .Where(type => DoesTypeSupportInterface(type, desiredType));
    }

    public static IEnumerable<Type> NonAbstractTypesImplementingInterface(Type desiredType)
    {
        return TypesImplementingInterface(desiredType).Where(t => !t.IsAbstract);
    }
}
Run Code Online (Sandbox Code Playgroud)


tag*_*s2k 5

编辑:我刚刚看到编辑,以澄清原始问题是减少迭代/代码,这一切都很好,作为一个练习,但在现实世界的情况下,你将希望最快的实现,无论如何底层LINQ看起来有多酷.

这是我的Utils方法,用于迭代加载的类型.它处理常规类和接口,如果您在自己的/第三方代码库中寻找实现,则excludeSystemTypes选项可以大大加快速度.

public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
    List<Type> list = new List<Type>();
    IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
    while (enumerator.MoveNext()) {
        try {
            Type[] types = ((Assembly) enumerator.Current).GetTypes();
            if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
                IEnumerator enumerator2 = types.GetEnumerator();
                while (enumerator2.MoveNext()) {
                    Type current = (Type) enumerator2.Current;
                    if (type.IsInterface) {
                        if (current.GetInterface(type.FullName) != null) {
                            list.Add(current);
                        }
                    } else if (current.IsSubclassOf(type)) {
                        list.Add(current);
                    }
                }
            }
        } catch {
        }
    }
    return list;
}
Run Code Online (Sandbox Code Playgroud)

我承认,它不漂亮.

  • 枚举器实现IDisposable,它不会在try/finally中处理.最好使用foreach或linq. (2认同)