【问题标题】:Getting all types that implement an interface获取所有实现接口的类型
【发布时间】:2010-09-06 19:19:06
【问题描述】:

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

这是我要重写的:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff

【问题讨论】:

  • 示例代码有效吗?我的 if 条件有误报。
  • 上面代码中的 if 语句将始终为 false,因为您正在测试 Type 类 (t) 的实例是否实现了您的接口,除非 Type 继承 IMyInterface(在这种情况下它永远是真的)。​​

标签: c# optimization reflection lambda c#-3.0


【解决方案1】:

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

类似:

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
        }
    }
}

【讨论】:

    【解决方案2】:

    我在 c# 3.0 中是这样的 :)

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

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

    loop assemblies  
     loop types  
      see if implemented.
    

    【讨论】:

    • 请注意,该列表可能还包括接口本身。将最后一行更改为 .Where(p => type.IsAssignableFrom(p) && !p.IsInterface); 以将其过滤掉(或 p.IsClass)。
    • 注意:这个答案是错误的!,这个检查“分配兼容性”而不是接口是否实现。例如List<string> 没有实现IEnumerable<object>,但是由于协方差,这个方法在.Net 4.0 中会返回true,这确实是错误的。 Correct answer is here
    • @SriramSakthivel 首先,没有指定通用值。其次,这个问题早于协方差。第三,你假设协变回报不是他们想要的。
    • 你说得对,darren,我知道这是一个老话题,我只是注册了我的评论,只是为了让未来的用户意识到存在这样的问题。不是为了冒犯你。正如问题标题所说,如果 OP 要求 Getting all types that implement an interface 此代码没有这样做。但毫无疑问,几乎所有情况它都有效。正如我所说,也有极端案例。只是要意识到这一点;
    • 还需要确保该类不是抽象类 => .Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
    【解决方案3】:

    编辑:我刚刚看到编辑澄清最初的问题是减少迭代/代码,这一切都很好,作为一个练习,但在现实世界的情况下,你会想要最快的实现,无论底层 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;
    }
    

    不漂亮,我承认。

    【讨论】:

    • 枚举器实现 IDisposable,它没有在 try/finally 中处理。最好使用 foreach 或 linq。
    • 你为什么要同时测试excludeSystemTypes两次if
    【解决方案4】:

    没有简单的方法(就性能而言)来做你想做的事。

    反射主要与程序集和类型一起使用,因此您必须获取程序集的所有类型并查询它们以获得正确的接口。这是一个例子:

    Assembly asm = Assembly.Load("MyAssembly");
    Type[] types = asm.GetTypes();
    Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);
    

    这将为您提供在 Assembly MyAssembly 中实现 IMyInterface 的所有类型

    【讨论】:

      【解决方案5】:

      您可以使用一些 LINQ 来获取列表:

      var types = from type in this.GetType().Assembly.GetTypes()
                  where type is ISomeInterface
                  select type;
      

      但真的,这样更易​​读吗?

      【讨论】:

      • 如果它有效,它可能更具可读性。不幸的是,您的 where 子句正在检查 System.Type 类的实例是否实现了 ISomeInterface,这永远不会为真,除非 ISomeInterface 真的是 IReflect 或 ICustomAttributeProvider,在这种情况下它将始终为真。
      • Carl Nayak 上面的答案有更正 where 子句的答案:IsAssignableFrom。答案很容易出错。
      【解决方案6】:

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

      var results = from type in someAssembly.GetTypes()
                    where typeof(IFoo).IsAssignableFrom(type)
                    select type;
      

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

      where type is IFoo
      

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

      此外,Adam Wright 的建议(目前被标记为答案)也是不正确的,原因相同。在运行时,您会看到 0 个类型返回,因为所有 System.Type 实例都不是 IFoo 实现者。

      【讨论】:

      • 这是正确答案。接受的答案不起作用。
      • 这也返回接口本身。看起来问题是只要求返回接口继承的类的类型,而不是接口本身。最好的。
      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        这里的其他答案使用IsAssignableFrom。您还可以使用System 命名空间中的FindInterfaces,如here 所述。

        这是一个检查当前执行程序集文件夹中所有程序集的示例,查找实现特定接口的类(为清楚起见避免使用 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();
        }
        

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

        【讨论】:

        • 这个寻找字符串接口名称,这是我正在寻找的。​​span>
        • 在不同域中加载程序集时有效,因为类型必须序列化为字符串。太棒了!
        • 我得到:无法解析对程序集“System.Core,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089”的依赖,因为它没有被预加载。使用 ReflectionOnly API 时,必须通过 ReflectionOnlyAssemblyResolve 事件预加载或按需加载依赖程序集。
        【解决方案9】:

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

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

        【讨论】:

        • 您假设程序集位于主可执行文件中。不是额外的项目。通过一堆迭代,您也进行了不必要的迭代。最好让框架完成繁重的工作。然后在找到时进一步过滤。如果相关,请更新您的答案。包括 List 推理。 var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(mytype => typeof(myInterface).IsAssignableFrom(mytype) && mytype.GetInterfaces().Contains(typeof(myInterface) )); foreach(var item in items) Console.Log(item.Name);
        【解决方案10】:

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

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

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

        所以说我们有:

        Class A // in AssemblyA
        Class B : Class A, IMyInterface // in AssemblyB
        Class C // in AssemblyC which references AssemblyB but not AssemblyA
        

        如果在ClassC 中,也就是在AssemblyC 中,我们会按照接受的答案做一些事情:

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

        然后它会抛出一个ReflectionTypeLoadException

        这是因为如果在 AssemblyC 中没有对 AssemblyA 的引用,您将无法:

        var bType = typeof(ClassB);
        var bClass = (ClassB)Activator.CreateInstance(bType);
        

        换句话说,ClassB 不是 可加载的,这是对 GetTypes 的调用检查并抛出的东西。

        因此,为了安全地将结果集限定为可加载类型,那么根据这篇Phil Haacked 文章Get All Types in an AssemblyJon Skeet code,您将改为执行以下操作:

        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);
                }
            }
        }
        

        然后:

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

        【讨论】:

        • 这帮助我处理了一个非常奇怪的问题,在我的测试项目中,GetTypes 会失败,并且只在我们的 CI 环境中。 GetLoadableTypes 是对此解决方案的修复。该错误不会在本地环境中重现,它是:System.Reflection.ReflectionTypeLoadException:无法加载一种或多种请求的类型。检索 LoaderExceptions 属性以获取更多信息。更具体地说,它抱怨有一种类型没有具体实现,并且发生在单元测试项目中。谢谢!
        • 这个答案应该被标记为解决方案,它今天救了我的命,因为就像@Lari Tuomisto 说的那样,在本地环境中我们无法重新生产类似的错误
        • 以防它帮助别人:这个解决方案对我有用,但我不得不修改它以从列表中删除接口类型。我想为所有这些都激活CreateInstance,当它试图创建实际界面时抛出了一个异常(当我认为实际界面在这个解决方案中不碍事时,这让我困惑了一段时间)。于是我把代码改成了GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t =&gt; !(t.Equals(interfaceType))).ToList();
        【解决方案11】:

        我在 linq 代码中遇到异常,所以我这样做(没有复杂的扩展):

        private static IList<Type> loadAllImplementingTypes(Type[] interfaces)
        {
            IList<Type> implementingTypes = new List<Type>();
        
            // find all types
            foreach (var interfaceType in interfaces)
                foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
                    try
                    {
                        foreach (var currentType in currentAsm.GetTypes())
                            if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
                                implementingTypes.Add(currentType);
                    }
                    catch { }
        
            return implementingTypes;
        }
        

        【讨论】:

          【解决方案12】:

          其他答案不适用于通用界面

          这个可以,只需将 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();
          

          所以

          AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
          

          我们得到所有的程序集

          !x.IsInterface && !x.IsAbstract
          

          用于排除接口和抽象的和

          .Select(x => x.Name).ToList();
          

          将它们列在一个列表中。

          【讨论】:

          • 请解释您的解决方案是如何工作的以及为什么它优于所有其他答案。
          • 它没有优劣之分,其他答案对我不起作用,我懒得分享。
          • 我的评论只是关于您的答案是纯代码,所以我要求您添加一些解释。
          【解决方案13】:

          选择装配位置时会更好。如果您知道所有已实现的接口都在同一个 Assembly.DefinedTypes 中,请过滤大多数程序集。

          // We get the assembly through the base class
          var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;
          
          // we filter the defined classes according to the interfaces they implement
          var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();
          

          By Can Bilgin

          【讨论】:

            【解决方案14】:
               public IList<T> GetClassByType<T>()
               {
                    return AppDomain.CurrentDomain.GetAssemblies()
                                      .SelectMany(s => s.GetTypes())
                                      .ToList(p => typeof(T)
                                      .IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface)
                                      .SelectList(c => (T)Activator.CreateInstance(c));
               }
            

            【讨论】:

              【解决方案15】:

              【讨论】:

                【解决方案16】:

                已经有很多有效的答案,但我想添加另一个实现作为类型扩展和一个单元测试列表来演示不同的场景:

                public static class TypeExtensions
                {
                    public static IEnumerable<Type> GetAllTypes(this Type type)
                    {
                        var typeInfo = type.GetTypeInfo();
                        var allTypes = GetAllImplementedTypes(type).Concat(typeInfo.ImplementedInterfaces);
                        return allTypes;
                    }
                
                    private static IEnumerable<Type> GetAllImplementedTypes(Type type)
                    {
                        yield return type;
                        var typeInfo = type.GetTypeInfo();
                        var baseType = typeInfo.BaseType;
                        if (baseType != null)
                        {
                            foreach (var foundType in GetAllImplementedTypes(baseType))
                            {
                                yield return foundType;
                            }
                        }
                    }
                }
                

                该算法支持以下场景:

                public static class GetAllTypesTests
                {
                    public class Given_A_Sample_Standalone_Class_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(SampleStandalone);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(SampleStandalone),
                                    typeof(object)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Abstract_Base_Class_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(SampleBase);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(SampleBase),
                                    typeof(object)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Child_Class_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(SampleChild);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(SampleChild),
                                    typeof(SampleBase),
                                    typeof(object)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Base_Interface_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(ISampleBase);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(ISampleBase)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Child_Interface_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(ISampleChild);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(ISampleBase),
                                    typeof(ISampleChild)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Implementation_Class_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        protected override void Given()
                        {
                            _sut = typeof(SampleImplementation);
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(SampleImplementation),
                                    typeof(SampleChild),
                                    typeof(SampleBase),
                                    typeof(ISampleChild),
                                    typeof(ISampleBase),
                                    typeof(object)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    public class Given_A_Sample_Interface_Instance_Type_When_Getting_All_Types
                        : Given_When_Then_Test
                    {
                        private Type _sut;
                        private IEnumerable<Type> _expectedTypes;
                        private IEnumerable<Type> _result;
                
                        class Foo : ISampleChild { }
                
                        protected override void Given()
                        {
                            var foo = new Foo();
                            _sut = foo.GetType();
                
                            _expectedTypes =
                                new List<Type>
                                {
                                    typeof(Foo),
                                    typeof(ISampleChild),
                                    typeof(ISampleBase),
                                    typeof(object)
                                };
                        }
                
                        protected override void When()
                        {
                            _result = _sut.GetAllTypes();
                        }
                
                        [Fact]
                        public void Then_It_Should_Return_The_Right_Type()
                        {
                            _result.Should().BeEquivalentTo(_expectedTypes);
                        }
                    }
                
                    sealed class SampleStandalone { }
                    abstract class SampleBase { }
                    class SampleChild : SampleBase { }
                    interface ISampleBase { }
                    interface ISampleChild : ISampleBase { }
                    class SampleImplementation : SampleChild, ISampleChild { }
                }
                

                【讨论】:

                  【解决方案17】:

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

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

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

                  【讨论】:

                    【解决方案18】:

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

                    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);
                        }
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2015-11-22
                      • 1970-01-01
                      • 2016-11-21
                      • 1970-01-01
                      • 2016-05-13
                      • 2014-07-31
                      • 2014-07-07
                      • 1970-01-01
                      相关资源
                      最近更新 更多