获取实现接口的所有类型


553

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

这是我要重写的内容:

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

1
示例代码是否有效?我对您的if条件有误报。
Orionii皇帝'12

3
上面的代码中的if语句将始终为false,因为您正在测试Type类(t)的实例是否实现了您的接口,除非Type继承了IMyInterface(在这种情况下它将始终为true),否则它不会实现。
Liazy 2013年

Answers:


806

我的将是在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.

194
请注意,该列表可能还包括接口本身。更改最后一行以.Where(p => type.IsAssignableFrom(p) && !p.IsInterface);将其过滤掉(或p.IsClass)。
jtpereyda

39
注意:此答案是错误的!,这将检查“分配兼容性”,而不是是否实现了接口。例如List<string>,未实现,IEnumerable<object>但由于协方差,该方法在.Net 4.0中将返回true,这的确是错误的。正确的答案在这里
Sriram Sakthivel 2014年

20
首先,@ SriramSakthivel,未指定通用值。第二,这个问题早于协方差。第三,假设协变量收益不是他们想要的。
达伦·科普

24
你们绝对是对的,达伦,我知道这是一个老话题,我只是注册了我的评论,只是为了将来的用户意识到存在这种问题。不要冒犯你。正如问题标题所指出的,OP是否要求获取实现接口的所有类型,因此此代码没有做到这一点。但毫无疑问,几乎所有情况下它都有效。正如我所说,也有一些极端情况。只是要意识到这一点;
Sriram Sakthivel 2014年

9
还需要确保该类不是抽象=>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis

66

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

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

5
您假设程序集在主可执行文件中。没有其他项目。您还可以通过一堆迭代不必要地进行迭代。最好让框架来做繁重的工作。然后在找到后进一步过滤。如果相关,请更新您的答案。包括List <T>推理。var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies()。SelectMany(x => x.GetTypes())。Where(mytype => typeof(myInterface).IsAssignableFrom(mytype)&& mytype.GetInterfaces()。Contains(typeof(myInterface) )); foreach(项目中的可变项目)Console.Log(item.Name);
TamusJRoyce 2014年

58

要在实现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。那会得到您预期的结果。

同样,出于同样的原因,目前被标记为答案的亚当·赖特的建议也是错误的。在运行时,您会看到0类型返回,因为所有System.Type实例都不是IFoo实现者。


58

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

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

一个无法激活类型的经典示例是,当返回的类型derived来自,base但在与的类型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

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

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

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

因此,为了安全地将结果集限定为可装入类型,然后按照此Phil Haacked文章“在Assembly中获取所有类型”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);
        }
    }
}

然后:

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

3
这帮助我解决了一个非常奇怪的问题,在我的测试项目中,GetTypes只会在我们的CI环境中失败。GetLoadableTypes是此解决方案的修复。该错误在本地环境中不可再现,它是这样的:System.Reflection.ReflectionTypeLoadException:无法加载一个或多个请求的类型。检索LoaderExceptions属性以获取更多信息。更具体地说,它抱怨说有一个没有具体实现的类型,它发生在单元测试项目中。谢谢你!
Lari Tuomisto

2
这个答案应该标记为解决方案,它今天挽救了我的屁股,因为就像@Lari Tuomisto所说,在本地环境中,我们无法重新产生类似的错误
Lightning3

3
万一它对其他人有帮助:这个解决方案对我有用,但是我必须对其进行修改以从列表中删除接口类型。我想CreateInstance为所有它们激活,并且在尝试创建实际接口时引发了异常(当我认为实际接口不适合该解决方案时,这让我感到困惑)。因此,我将代码更改为GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();
哈维尔·佩尼亚

21

其他答案在这里使用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();
}

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


这是我正在寻找的字符串接口名称。
sendhil 2013年

在将程序集加载到其他域中时有效,因为类型必须序列化为字符串。太棒了!
TamusJRoyce,2015年

我得到:无法解析对程序集'System.Core,版本= 4.0.0.0,Culture =中性,PublicKeyToken = b77a5c561934e089'的依赖,因为它尚未预加载。使用ReflectionOnly API时,必须通过ReflectionOnlyAssemblyResolve事件预加载或根据需要加载相关程序集。
bkwdesign

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

8

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

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

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

我承认,这并不漂亮。


2
枚举器实现的IDisposable不会在try / finally中处理。最好使用foreach或linq。
TamusJRoyce 2015年

为什么要一次进行excludeSystemTypes两次测试if
NetMage

4

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

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

将它们放在列表中。


请说明您的解决方案如何工作,以及为什么它优于所有其他答案。
LukasKörfer18年

它不是优劣,其他答案对我没有用,我不愿分享。
Antonin GAVREL

我的评论只是关于您的答案仅是代码的,因此我要求您添加一些解释。
LukasKörfer18年

2

没有简单的方法(就性能而言)来完成您想做的事情。

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

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

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


2

选择组装位置时更好。如果您知道所有已实现的接口都在同一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();

坎·比尔金



1

已经有许多有效的答案,但是我想添加另一种实现作为Type扩展和一系列单元测试来演示不同的场景:

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

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

0

我在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;
}

-3

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

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

但是真的,这更具可读性吗?


6
如果可行,它可能会更具可读性。不幸的是,您的where子句正在检查System.Type类的实例是否实现了ISomeInterface,除非ISomeInterface确实是IReflect或ICustomAttributeProvider,否则它永远不会是true。
乔尔·穆勒

卡尔·纳亚克(Carl Nayak)的上述答案具有纠正where子句的答案:IsAssignableFrom。容易出错的答案。
TamusJRoyce,2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.