Bắt tất cả các loại thực hiện một giao diện


553

Sử dụng sự phản chiếu, làm cách nào tôi có thể nhận được tất cả các loại thực hiện giao diện với C # 3.0 / .NET 3.5 với ít mã nhất và giảm thiểu số lần lặp?

Đây là những gì tôi muốn viết lại:

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

1
Mã ví dụ có hoạt động không? Tôi đã có những tiêu cực sai với điều kiện if của bạn.
Hoàng đế Orionii

3
Câu lệnh if trong đoạn mã trên sẽ luôn sai vì bạn đang kiểm tra nếu một thể hiện của lớp Type (t) thực hiện giao diện của bạn mà nó sẽ không trừ khi Kiểu kế thừa IMyInterface (trong trường hợp đó nó sẽ luôn đúng).
Liazy

Câu trả lời:


807

Của tôi sẽ là cái này trong c # 3.0 :)

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

Về cơ bản, số lần lặp ít nhất sẽ luôn là:

loop assemblies  
 loop types  
  see if implemented.

194
Lưu ý rằng danh sách cũng có thể bao gồm chính giao diện. Thay đổi dòng cuối cùng .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);để lọc nó ra (hoặc p.IsClass).
jtpereyda

39
Lưu ý: Câu trả lời này là sai!, Kiểm tra này "Tương thích chuyển nhượng" không phải là giao diện có được triển khai hay không. Ví dụ List<string>không thực hiện IEnumerable<object>nhưng phương thức này sẽ trả về true trong .Net 4.0 do hiệp phương sai thực sự sai. Câu trả lời đúng là ở đây
Sriram Sakth Xoay

20
@SriramSakth Xoay trước hết, các giá trị chung không được chỉ định. Thứ hai, câu hỏi này trước ngày hiệp phương sai. Thứ ba, bạn đưa ra giả định rằng lợi nhuận covariant không phải là thứ họ muốn.
Darren Kopp

24
Bạn hoàn toàn đúng, tôi biết đây là một chủ đề cũ, tôi chỉ đăng ký nhận xét của mình cho người dùng trong tương lai để nhận ra vấn đề như vậy tồn tại. Không xúc phạm bạn. và như tiêu đề câu hỏi cho biết nếu OP đang yêu cầu Nhận tất cả các loại triển khai giao diện thì mã này sẽ không làm điều đó. nhưng hầu như tất cả các trường hợp nó hoạt động , không có nghi ngờ. Có những trường hợp góc quá như tôi đã nói. Chỉ cần nhận thức được nó;
Sriram Sakth Xoay

9
Cũng cần phải đảm bảo rằng lớp không trừu tượng =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis

66

Điều này làm việc cho tôi. Nó lặp lại mặc dù các lớp và kiểm tra xem chúng có bị tách khỏi myInterface không

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

5
Bạn đang giả định lắp ráp là trong thực thi chính. Không phải là một dự án bổ sung. Bạn cũng đang lặp đi lặp lại một cách không cần thiết mặc dù một loạt các lần lặp. Nó là tốt hơn để có khung làm việc nặng. Sau đó lọc xuống xa hơn khi tìm thấy. Nếu có liên quan, xin vui lòng cập nhật câu trả lời của bạn. Bao gồm Danh sách <T> lý luận. var classTypesImcellenceationInterface = AppDomain.CiverseDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Trong đó (mytype => typeof (myInterface) .IsAssignableFrom (mytype của tôi) )); foreach (var item in items) Console.Log (item.Name);
TamusJRoyce

58

Để tìm tất cả các loại trong một hội đồng thực hiện giao diện IFoo:

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

Lưu ý rằng đề nghị của Ryan Rinaldi là không chính xác. Nó sẽ trả về 0 loại. Bạn không thể viết

where type is IFoo

bởi vì kiểu là một thể hiện System.Type và sẽ không bao giờ thuộc kiểu IFoo. Thay vào đó, bạn kiểm tra xem IFoo có được gán từ loại không. Điều đó sẽ nhận được kết quả mong đợi của bạn.

Ngoài ra, đề nghị của Adam Wright, hiện được đánh dấu là câu trả lời, cũng không chính xác và vì lý do tương tự. Khi chạy, bạn sẽ thấy 0 loại quay trở lại, bởi vì tất cả các trường hợp System.Type không phải là người triển khai IFoo.


58

Tôi đánh giá cao đây là một câu hỏi rất cũ nhưng tôi nghĩ rằng tôi sẽ thêm một câu trả lời khác cho người dùng trong tương lai vì tất cả các câu trả lời cho đến nay đều sử dụng một số hình thức Assembly.GetTypes.

Trong khi GetTypes () thực sự sẽ trả về tất cả các loại, điều đó không nhất thiết có nghĩa là bạn có thể kích hoạt chúng và do đó có khả năng ném một ReflectionTypeLoadException.

Một ví dụ điển hình cho việc không thể kích hoạt một loại sẽ là khi loại trở lại là derivedtừ basenhưng baseđược định nghĩa trong một hội đồng khác với derived, tham khảo một hội rằng gọi lắp ráp thì không.

Vì vậy, nói rằng chúng ta có:

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

Nếu trong ClassCđó là trong AssemblyCchúng ta sau đó làm một cái gì đó theo câu trả lời được chấp nhận:

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

Rồi nó sẽ ném a ReflectionTypeLoadException.

Điều này là do không có tài liệu tham khảo AssemblyA trong AssemblyCbạn sẽ không thể:

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

Nói cách khác, ClassBkhông thể tải được , đó là điều mà lệnh gọi GetTypes kiểm tra và thực hiện.

Vì vậy, để đủ điều kiện an toàn kết quả được đặt cho các loại có thể tải sau đó, theo bài viết này Phil Haacked Nhận tất cả các loại trong một hộimã Jon Skeet, thay vào đó bạn sẽ làm một cái gì đó như:

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

Và sau đó:

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

3
Điều này giúp tôi đối phó với một vấn đề siêu kỳ lạ, trong đó trong dự án thử nghiệm của tôi, GetTypes sẽ thất bại và chỉ trong môi trường CI của chúng tôi. GetLoadableTypes là một sửa chữa cho giải pháp này. Lỗi sẽ không thể tái tạo trong môi trường cục bộ và đó là lỗi: System.Reflection.ReflectionTypeLoadException: Không thể tải một hoặc nhiều loại được yêu cầu. Truy xuất thuộc tính LoaderExceptions để biết thêm thông tin. Cụ thể hơn, nó đã phàn nàn rằng có một loại không có triển khai cụ thể và nó đã xảy ra trong dự án thử nghiệm đơn vị. Cám ơn vì cái này!
Lari Tuomisto

2
Câu trả lời này nên được đánh dấu là giải pháp, nó đã lưu vào mông tôi hôm nay, vì như @Lari Tuomisto đã nói, trên env địa phương, chúng tôi không thể tái sản xuất lỗi tương tự
Lightning3

3
Trong trường hợp nó giúp được người khác: giải pháp này hiệu quả với tôi, nhưng tôi đã phải sửa đổi nó để loại bỏ giao diện khỏi danh sách. Tôi muốn kích hoạt CreateInstancecho tất cả chúng, và một ngoại lệ đã được đưa ra khi nó đang cố gắng tạo giao diện thực tế (điều này khiến tôi bối rối trong một thời gian khi tôi nghĩ rằng giao diện thực tế không phù hợp với giải pháp này). Vì vậy, tôi đã thay đổi mã thành GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña

21

Câu trả lời khác ở đây sử dụng IsAssignableFrom. Bạn cũng có thể sử dụng FindInterfacestừ Systemkhông gian tên, như được mô tả ở đây .

Dưới đây là một ví dụ kiểm tra tất cả các hội đồng trong thư mục của hội đồng hiện đang thực thi, tìm kiếm các lớp thực hiện một giao diện nhất định (tránh LINQ cho rõ ràng).

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

Bạn có thể thiết lập một danh sách các giao diện nếu bạn muốn khớp nhiều giao diện.


Cái này tìm tên giao diện chuỗi, cái mà tôi đang tìm.
Senthil

Hoạt động khi tải một cụm trong một miền khác, vì loại phải được nối tiếp thành một chuỗi. rất tuyệt vời!
TamusJRoyce

Tôi nhận được: Không thể giải quyết sự phụ thuộc vào lắp ráp 'System.Core, Version = 4.0.0.0, Culture = trung tính, PublicKeyToken = b77a5c561934e089' vì nó chưa được tải trước. Khi sử dụng API ReflectionOnly, các hội đồng phụ thuộc phải được tải trước hoặc tải theo yêu cầu thông qua sự kiện ReflectionOnlyAss lanhResolve.
bkwdesign

18

lặp qua tất cả các hội đồng được tải, lặp qua tất cả các loại của chúng và kiểm tra xem chúng có thực hiện giao diện không.

cái gì đó như:

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

Điều này làm việc cho tôi (nếu bạn muốn bạn có thể loại trừ các loại hệ thống trong tra cứu):

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

5

Chỉnh sửa: Tôi vừa xem bản chỉnh sửa để làm rõ rằng câu hỏi ban đầu là về việc giảm số lần lặp / mã và đó là tất cả tốt và tốt như một bài tập, nhưng trong các tình huống trong thế giới thực, bạn sẽ muốn thực hiện nhanh nhất, bất kể làm thế nào mát mẻ LINQ cơ bản trông.

Đây là phương pháp Utils của tôi để lặp qua các loại được tải. Nó xử lý các lớp thông thường cũng như các giao diện và tùy chọn ElimSystemTypes tăng tốc mọi thứ lên rất nhiều nếu bạn đang tìm kiếm các triển khai trong cơ sở mã của bên thứ ba / của riêng bạn.

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

Nó không đẹp, tôi sẽ thừa nhận.


2
Các điều tra viên thực hiện IDis Dùng mà không được xử lý trong một lần thử / cuối cùng. Nó là tốt hơn để sử dụng một foreach hoặc linq.
TamusJRoyce

Tại sao bạn thử nghiệm excludeSystemTypeshai lần trong một if?
NetMage

4

Câu trả lời khác không hoạt động với một giao diện chung .

Cái này không, chỉ cần thay thế typeof (ISomeInterface) bằng 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();

Vì vậy

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

chúng tôi nhận được tất cả các hội đồng

!x.IsInterface && !x.IsAbstract

được sử dụng để loại trừ giao diện và những cái trừu tượng và

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

để có chúng trong một danh sách.


Vui lòng giải thích cách giải pháp của bạn hoạt động và tại sao nó tốt hơn tất cả các câu trả lời khác.
Lukas Körfer

Nó không vượt trội hay thấp hơn, những câu trả lời khác không phù hợp với tôi và tôi bận tâm chia sẻ nó.
Antonin GAVREL

Nhận xét của tôi chỉ là về câu trả lời của bạn chỉ ở dạng mã, vì vậy tôi đã yêu cầu bạn thêm một số lời giải thích.
Lukas Körfer

2

Không có cách nào dễ dàng (về hiệu suất) để làm những gì bạn muốn làm.

Sự phản chiếu hoạt động với các cụm và các loại chủ yếu vì vậy bạn sẽ phải lấy tất cả các loại lắp ráp và truy vấn chúng cho giao diện phù hợp. Đây là một ví dụ:

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

Điều đó sẽ giúp bạn có tất cả các loại triển khai IMyInterface trong hội MyAssugging


2

Thậm chí tốt hơn khi chọn vị trí lắp ráp. Lọc hầu hết các hội đồng nếu bạn biết tất cả các giao diện được triển khai của bạn nằm trong cùng một hội.D.DTTpes.

// 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();

Bằng cách có thể



1

Đã có nhiều câu trả lời hợp lệ nhưng tôi muốn thêm triển khai bao phấn dưới dạng tiện ích mở rộng Loại và danh sách các thử nghiệm đơn vị để trình bày các kịch bản khác nhau:

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

Thuật toán này hỗ trợ các tình huống sau:

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

Tôi có ngoại lệ trong mã linq vì vậy tôi thực hiện theo cách này (không có phần mở rộng phức tạp):

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

Bạn có thể sử dụng một số LINQ để lấy danh sách:

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

Nhưng thực sự, điều đó có dễ đọc hơn không?


6
Nó có thể dễ đọc hơn, nếu nó hoạt động. Thật không may, mệnh đề where của bạn đang kiểm tra xem liệu một thể hiện của lớp System.Type thực hiện ISomeInterface, điều này sẽ không bao giờ đúng, trừ khi ISomeInterface thực sự là IReflect hoặc ICustomAttributionProvider, trong trường hợp đó nó sẽ luôn đúng.
Joel Mueller

Câu trả lời của Carl Nayak ở trên có câu trả lời cho việc sửa mệnh đề where: IsAssignableFrom. Dễ mắc lỗi cho một câu trả lời.
TamusJRoyce
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.