Mendapatkan semua jenis yang mengimplementasikan antarmuka

554

Menggunakan refleksi, bagaimana saya bisa mendapatkan semua jenis yang mengimplementasikan antarmuka dengan C # 3.0 / .NET 3.5 dengan kode paling sedikit, dan meminimalkan iterasi?

Inilah yang ingin saya tulis ulang:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
juan
sumber
1
Apakah kode contoh berfungsi? Saya mendapat negatif palsu dengan kondisi if Anda.
Kaisar Orionii
3
Pernyataan if dalam kode di atas akan selalu salah karena Anda menguji jika instance dari kelas Type (t) mengimplementasikan antarmuka Anda yang tidak akan kecuali Type mewarisi IMyInterface (dalam hal ini akan selalu benar).
Liazy

Jawaban:

808

Milik saya ini di c # 3.0 :)

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

Pada dasarnya, jumlah iterasi paling sedikit akan selalu:

loop assemblies  
 loop types  
  see if implemented.
Darren Kopp
sumber
194
Perhatikan bahwa daftar ini juga dapat menyertakan antarmuka itu sendiri. Ubah baris terakhir .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);untuk memfilternya (atau p.IsClass).
jtpereyda
39
Catatan: Jawaban ini salah !, ini memeriksa "Kompatibilitas penugasan" bukan apakah antarmuka diimplementasikan tidak. Misalnya List<string>tidak menerapkan IEnumerable<object>tetapi metode ini akan mengembalikan true di. Net 4.0 karena kovarian yang memang salah. Jawaban yang benar ada di sini
Sriram Sakthivel
20
@SriramSakthivel pertama, nilai-nilai umum tidak ditentukan. Kedua, pertanyaan ini pra-tanggal kovarian. Ketiga, Anda membuat asumsi bahwa pengembalian kovarian bukanlah sesuatu yang mereka inginkan.
Darren Kopp
24
Anda benar sekali darren, saya tahu ini adalah utas lama, saya baru mendaftarkan komentar saya hanya untuk pengguna di masa depan agar masalah seperti itu ada. Tidak menyinggung Anda. dan seperti yang dikatakan judul pertanyaan jika OP meminta untuk Mendapatkan semua jenis yang mengimplementasikan antarmuka kode ini tidak melakukan itu. tetapi hampir semua kasus berhasil , tidak diragukan lagi. ada juga kasus sudut seperti yang saya katakan. Hanya untuk menyadarinya;
Sriram Sakthivel
9
Juga perlu memastikan bahwa kelas tidak abstrak =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis
66

Ini berhasil untuk saya. Itu loop meskipun kelas dan memeriksa untuk melihat apakah mereka berasal dari myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }
Ben Watkins
sumber
5
Anda mengasumsikan bahwa rakitan berada dalam executable utama. Bukan proyek tambahan. Anda juga melakukan iterasi tanpa perlu melalui banyak iterasi. Lebih baik memiliki kerangka kerja melakukan angkat berat. Kemudian saring lebih jauh ketika ditemukan. Jika relevan, harap perbarui jawaban Anda. Cantumkan Daftar alasan <T>. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). Di mana (mytype => typeof (myInterface) .IsAsableableFrom (mytype) && mytype.terterfaces (). )); foreach (item var dalam item) Console.Log (item.Name);
TamusJRoyce
58

Untuk menemukan semua tipe dalam majelis yang mengimplementasikan antarmuka IFoo:

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

Perhatikan bahwa saran Ryan Rinaldi salah. Ini akan mengembalikan 0 jenis. Anda tidak dapat menulis

where type is IFoo

karena type adalah instance System.Type, dan tidak akan pernah bertipe IFoo. Sebagai gantinya, Anda memeriksa untuk melihat apakah IFoo dapat ditentukan dari jenisnya. Itu akan mendapatkan hasil yang Anda harapkan.

Juga, saran Adam Wright, yang saat ini ditandai sebagai jawabannya, juga tidak benar, dan karena alasan yang sama. Saat runtime, Anda akan melihat 0 jenis kembali, karena semua instans System.Type bukan implementor IFoo.

Yehuda Gabriel Himango
sumber
58

Saya menghargai ini adalah pertanyaan yang sangat lama tapi saya pikir saya akan menambahkan jawaban lain untuk pengguna di masa depan karena semua jawaban sampai saat ini menggunakan beberapa bentuk Assembly.GetTypes.

Sementara GetTypes () memang akan mengembalikan semua jenis, itu tidak berarti Anda bisa mengaktifkannya dan dengan demikian berpotensi membuang a ReflectionTypeLoadException.

Contoh klasik untuk tidak dapat mengaktifkan suatu tipe adalah ketika tipe yang dikembalikan adalah deriveddari basetetapi basedidefinisikan dalam rakitan yang berbeda dari derived, rakitan yang tidak dirujuk oleh rakitan pemanggil.

Jadi katakan kita memiliki:

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

Jika di ClassCdalamnya ada AssemblyCmaka kita melakukan sesuatu sesuai jawaban yang diterima:

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

Maka itu akan melempar ReflectionTypeLoadException.

Ini karena tanpa referensi ke AssemblyA dalam AssemblyCAnda tidak akan dapat:

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

Dengan kata lain ClassBtidak dapat dimuat yang merupakan sesuatu yang dipanggil dan dilemparkan oleh panggilan untuk GetTypes.

Jadi untuk memenuhi syarat dengan aman hasil yang ditetapkan untuk jenis yang dapat di-load maka sesuai dengan artikel Phil Haacked ini Dapatkan Semua Jenis dalam Majelis dan kode Jon Skeet Anda malah akan melakukan sesuatu seperti:

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

Lalu:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}
rism
sumber
3
Ini membantu saya menangani masalah yang sangat aneh, di mana dalam proyek pengujian saya GetTypes akan gagal dan hanya di lingkungan CI kami. GetLoadableTypes adalah perbaikan untuk solusi ini. Kesalahan tidak dapat direproduksi di lingkungan lokal dan ini adalah ini: System.Reflection.ReflectionTypeLoadException: Tidak dapat memuat satu atau lebih dari jenis yang diminta. Ambil properti LoaderExceptions untuk informasi lebih lanjut. Lebih khusus lagi itu mengeluh bahwa ada jenis yang tidak memiliki implementasi konkret dan itu terjadi dalam proyek unit test. Terima kasih untuk ini!
Lari Tuomisto
2
Jawaban ini harus ditandai sebagai solusi, ini menyelamatkan pantat saya hari ini, karena seperti kata @Lari Tuomisto, pada env lokal kami tidak dapat membuat ulang kesalahan yang serupa
Lightning3
3
Seandainya itu membantu orang lain: solusi ini bekerja untuk saya, tetapi saya harus memodifikasinya untuk menghapus jenis antarmuka dari daftar. Saya ingin mengaktifkan CreateInstanceuntuk mereka semua, dan pengecualian dilemparkan ketika mencoba membuat antarmuka yang sebenarnya (yang membuat saya bingung untuk sementara waktu ketika saya pikir antarmuka yang sebenarnya tidak sesuai dengan solusi ini). Jadi saya mengubah kode menjadi GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña
21

Jawaban lain di sini digunakan IsAssignableFrom. Anda juga dapat menggunakan FindInterfacesdari Systemnamespace, seperti dijelaskan di sini .

Berikut adalah contoh yang memeriksa semua majelis di folder majelis yang saat ini menjalankan, mencari kelas yang mengimplementasikan antarmuka tertentu (menghindari LINQ untuk kejelasan).

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

Anda dapat mengatur daftar antarmuka jika Anda ingin mencocokkan lebih dari satu.

hillstuk
sumber
Yang ini mencari nama antarmuka string yang adalah apa yang saya cari.
Senthil
Berfungsi saat memuat rakitan di domain yang berbeda, karena jenisnya harus diserialisasi ke string. sangat luar biasa!
TamusJRoyce
Saya mendapatkan: Tidak dapat menyelesaikan ketergantungan ke assembly 'System.Core, Versi = 4.0.0.0, Culture = netral, PublicKeyToken = b77a5c561934e089' karena belum dimuat sebelumnya. Saat menggunakan API ReflectionOnly, majelis bergantung harus dipra-muat atau dimuat sesuai permintaan melalui acara ReflectionOnlyAssemblyResolve.
bkwdesign
18

loop melalui semua rakitan yang dimuat, loop melalui semua tipenya, dan periksa apakah mereka mengimplementasikan antarmuka.

sesuatu seperti:

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
        }
    }
}
Lasse V. Karlsen
sumber
8

Ini berfungsi untuk saya (jika Anda mau, Anda dapat mengecualikan jenis sistem dalam pencarian):

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

Sunting: Saya baru saja melihat hasil edit untuk mengklarifikasi bahwa pertanyaan awal adalah untuk pengurangan iterasi / kode dan itu semua baik dan bagus sebagai latihan, tetapi dalam situasi dunia nyata Anda akan menginginkan implementasi tercepat, terlepas dari bagaimana kerennya tampilan LINQ yang mendasarinya.

Inilah metode Utils saya untuk iterasi melalui tipe yang dimuat. Ini menangani kelas reguler serta antarmuka, dan opsi excludeSystemTypes mempercepat segalanya jika Anda mencari implementasi dalam basis kode Anda sendiri / pihak ketiga.

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

Tidak cantik, saya akui.

tags2k
sumber
2
Enumerator menerapkan IDisposable yang tidak dibuang pada percobaan / akhirnya. Lebih baik menggunakan foreach atau LINQ.
TamusJRoyce
Mengapa Anda menguji excludeSystemTypesdua kali dalam satu if?
NetMage
4

Jawaban lain tidak berfungsi dengan antarmuka umum .

Yang ini, cukup ganti typeof (ISomeInterface) dengan 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();

Begitu juga dengan

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

kami mendapatkan semua majelis

!x.IsInterface && !x.IsAbstract

digunakan untuk mengecualikan antarmuka dan yang abstrak dan

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

untuk memilikinya dalam daftar.

Antonin GAVREL
sumber
Tolong jelaskan bagaimana solusi Anda bekerja dan mengapa itu lebih unggul dari semua jawaban lainnya.
Lukas Körfer
Itu tidak unggul atau lebih rendah, jawaban lain tidak bekerja untuk saya dan saya repot-repot membagikannya.
Antonin GAVREL
Komentar saya hanya tentang jawaban Anda hanya kode, jadi saya meminta Anda untuk menambahkan beberapa penjelasan.
Lukas Körfer
2

Tidak ada cara mudah (dalam hal kinerja) untuk melakukan apa yang ingin Anda lakukan.

Refleksi bekerja dengan rakitan dan tipe terutama sehingga Anda harus mendapatkan semua jenis rakitan dan meminta mereka untuk antarmuka yang tepat. Ini sebuah contoh:

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

Itu akan memberi Anda semua jenis yang menerapkan IMyInterface di Majelis MyAssembly

Jorge Córdoba
sumber
2

Bahkan lebih baik ketika memilih lokasi Majelis. Saring sebagian besar rakitan jika Anda tahu semua antarmuka yang Anda implementasikan berada dalam Assembly.DefinedTypes yang sama.

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

Oleh Can Bilgin

pengguna489566
sumber
1

Sudah ada banyak jawaban yang valid tetapi saya ingin menambahkan implementasi anther sebagai ekstensi Jenis dan daftar tes unit untuk menunjukkan skenario yang berbeda:

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

Algoritma ini mendukung skenario berikut:

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 { }
}
diegosasw
sumber
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));
   }
Jonathan Santiago
sumber
0

Saya mendapat pengecualian dalam kode-linq jadi saya melakukannya dengan cara ini (tanpa ekstensi yang rumit):

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;
}
akop
sumber
-3

Anda bisa menggunakan beberapa LINQ untuk mendapatkan daftar:

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

Tapi sungguh, apakah itu lebih mudah dibaca?

Ryan Rinaldi
sumber
6
Mungkin lebih mudah dibaca, jika berhasil. Sayangnya, klausa mana Anda memeriksa untuk melihat apakah turunan dari kelas System.Type mengimplementasikan ISomeInterface, yang tidak akan pernah benar, kecuali ISomeInterface benar-benar IReflect atau ICustomAttributeProvider, dalam hal ini akan selalu benar.
Joel Mueller
Carl Nayak jawaban di atas memiliki jawaban untuk mengoreksi klausa mana: IsAssignableFrom. Kesalahan mudah untuk jawaban.
TamusJRoyce