Arabirim uygulayan tüm türleri elde etme


553

Yansımayı kullanarak, en az kod ile C # 3.0 / .NET 3.5 ile bir arabirim uygulayan ve yinelemeleri en aza indiren tüm türleri nasıl alabilirim?

Bu yeniden yazmak istiyorum:

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

1
Örnek kod çalışıyor mu? İf durumunuzla ilgili yanlış olumsuzluklarım var.
İmparator Orionii

3
Yukarıdaki koddaki if ifadesi her zaman yanlış olacaktır, çünkü Type sınıfının (t) bir örneğinin, Type, IMyInterface'i devralmadığı sürece arayüzünüzü uygulayıp uygulamadığını test edersiniz (bu durumda her zaman doğru olur).
Liazy

Yanıtlar:


807

Benim c # 3.0 bu olurdu :)

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

Temel olarak, en az yineleme miktarı her zaman olacaktır:

loop assemblies  
 loop types  
  see if implemented.

194
Listenin arabirimin kendisini de içerebileceğini unutmayın. .Where(p => type.IsAssignableFrom(p) && !p.IsInterface);Filtrelemek (veya p.IsClass) için son satırı değiştirin .
jtpereyda

39
Not: Bu cevap yanlış !, bu, "Atama uyumluluğu" nu arayüzün uygulanıp uygulanmadığını kontrol eder. Örneğin List<string>, uygulanmaz, IEnumerable<object>ancak bu yöntem .Net 4.0'da yanlış olan kovaryans nedeniyle true değerini döndürür. Doğru cevap burada
Sriram Sakthivel

20
@SriramSakthivel önce kapalı, genel değerler belirtilmedi. İkincisi, bu soru kovaryanstan önce gelir. Üçüncüsü, kovaryant dönüşün istedikleri bir şey olmadığı varsayımını yaparsınız.
Darren Kopp

24
Kesinlikle haklısın, bu eski bir konu olduğunu biliyorum, sadece gelecekteki kullanıcıların böyle bir sorunun varlığından haberdar olması için yorumumu kaydettim. Sizi rahatsız etmemek. ve soru başlığı dediği gibi OP bir arabirim uygulayan tüm türleri almak istiyorsa bu kod bunu yapmıyor. ancak hemen hemen tüm vakalar çalıştığını , hiç şüphesiz. Dediğim gibi köşe vakaları da var. Sadece farkında olmak için;
Sriram Sakthivel

9
Ayrıca sınıfın soyut olmadığından emin olmanız gerekecek =>.Where(p => type.IsAssignableFrom(p) && p.IsClass && !p.IsAbstract
Jonesopolis

66

Bu benim için çalıştı. Sınıflar olsa döngüler ve benim Arayüzümden yoksun olup olmadıklarını kontrol eder

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

5
Derlemenin ana yürütülebilir dosyada olduğunu varsayıyorsunuz. Ek bir proje değil. Ayrıca bir sürü iterasyon olsa da gereksiz yere yineliyorsunuz. Çerçevenin ağır kaldırmayı yapması daha iyidir. Ardından bulunduğunda daha da aşağı süzün. Alakalıysa, lütfen yanıtınızı güncelleyin. Liste <T> mantığını dahil et. var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies (). SelectMany (x => x.GetTypes ()). )); foreach (öğelerdeki öğe var) Console.Log (item.Name);
TamusJRoyce

58

Bir montajdaki IFoo arabirimini uygulayan tüm türleri bulmak için:

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

Ryan Rinaldi'nin önerisinin yanlış olduğunu unutmayın. 0 tür döndürür. Yazamazsın

where type is IFoo

çünkü tür bir System.Type örneğidir ve hiçbir zaman IFoo türünde olmayacaktır. Bunun yerine, IFoo'nun türden atanabilir olup olmadığını kontrol edersiniz. Bu beklenen sonuçları alır.

Ayrıca, Adam Wright'ın şu anda cevap olarak işaretlenen önerisi de yanlış ve aynı nedenle. Tüm System.Type örnekleri IFoo uygulayıcıları olmadığından çalışma zamanında 0 tür geri gelir.


58

Bunun çok eski bir soru olduğunu takdir ediyorum ama bugüne kadarki tüm cevaplar bir tür kullandığından gelecekteki kullanıcılar için başka bir cevap ekleyeceğimi düşündüm Assembly.GetTypes.

GetTypes () gerçekten tüm türleri döndürecek olsa da, onları etkinleştirebileceğiniz ve dolayısıyla potansiyel olarak a atabileceğiniz anlamına gelmez ReflectionTypeLoadException.

Bir türü etkinleştirememenin klasik bir örneği, döndürülen türün , çağrı derlemesinin başvuruda bulunmadığı bir derlemeden farklı olduğu derived, baseancak bu türden basefarklı bir derlemede tanımlandığı durumdur derived.

Diyelim ki elimizde:

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

İçinde ClassCise, AssemblyCkabul edilen cevaba göre bir şey yaparız:

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

Sonra a atacak ReflectionTypeLoadException.

Bunun nedeni için bir referans olmadan AssemblyA içinde AssemblyCsize mümkün olmaz:

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

Başka bir deyişle ClassBdeğil yüklenebilir GetTypes çek çağrısı ve üzerinde atar şeydir.

Yüklenebilir türler için sonuç kümesini güvenli bir şekilde nitelendirmek için, bu Phil Haacked makalesine göre Bir Montajdaki Tüm Türleri Al ve Jon Skeet koduna göre şöyle bir şey yaparsınız:

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

Ve sonra:

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

3
Bu, test projemde GetTypes'in başarısız olacağı ve sadece CI ortamımızdaki süper garip bir sorunla baş etmeme yardımcı oldu. GetLoadableTypes bu çözüm için bir düzeltmeydi. Hata yerel ortamda yeniden oluşturulamaz ve şuydu: System.Reflection.ReflectionTypeLoadException: İstenen türlerden biri veya daha fazlası yüklenemedi. Daha fazla bilgi için LoaderExceptions özelliğini alın. Daha spesifik olarak somut bir uygulaması olmayan bir tür olduğundan ve birim test projesinde meydana geldiğinden şikayetçiydi. Bunun için teşekkürler!
Lari Tuomisto

2
Bu cevap çözüm olarak işaretlenmeli, bugün kıçımı kurtardı, çünkü @Lari Tuomisto'nun dediği gibi, yerel env'de benzer hatayı yeniden üretemedik
Lightning3

3
Başka birine yardımcı olması durumunda: bu çözüm benim için çalıştı, ancak arayüz türünü listeden kaldırmak için değiştirmek zorunda kaldım. CreateInstanceHepsi için etkinleştirmek istedim ve gerçek arayüzü oluşturmaya çalışırken bir istisna atıldı (gerçek arayüz bu çözümde yolun dışında olduğunu düşündüğümde bir süredir kafam karıştı). Bu yüzden kodu olarak değiştirdim GetLoadableTypes(assembly).Where(interfaceType.IsAssignableFrom).Where(t => !(t.Equals(interfaceType))).ToList();.
Xavier Peña

21

Burada diğer cevaplar kullanılır IsAssignableFrom. Ayrıca kullanabilirsiniz FindInterfacesgelen Systemaçıklandığı gibi, ad burada .

Burada, şu anda yürütülen derlemenin klasöründeki tüm derlemeleri kontrol eden ve belirli bir arabirimi uygulayan sınıfları arayan (netlik için LINQ'dan kaçınan) bir örnek verilmiştir.

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

Birden fazla eşleştirmek istiyorsanız bir arabirim listesi oluşturabilirsiniz.


Bu, aradığım şey olan dize arabirimi adını arar.
54'te gönderildi

Tür bir dizeye serileştirilmesi gerektiğinden, bir montajı farklı bir etki alanına yüklerken çalışır. çok mükemmel!
TamusJRoyce

Ben olsun: 'System.Core, Sürüm = 4.0.0.0, Kültür = nötr, PublicKeyToken = b77a5c561934e089' derleme bağımlılığı çözülemiyor çünkü. ReflectionOnly API'lerini kullanırken, bağımlı derlemeler ReflectionOnlyAssemblyResolve olayı aracılığıyla önceden yüklenmeli veya isteğe bağlı olarak yüklenmelidir.
bkwdesign

18

yüklü tüm montajlar arasında geçiş yapın, tüm türleri arasında döngü yapın ve arabirimi uygulayıp uygulamadıklarını kontrol edin.

gibi bir şey:

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

Bu benim için çalıştı (isterseniz sistem türlerini aramada hariç tutabilirsiniz):

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

5

Düzenleme: Sadece orijinal sorunun yinelemelerin / kodun azaltılması için olduğunu ve bu bir alıştırma olarak iyi ve iyi olduğunu açıklamak için düzenlemeyi gördüm, ancak gerçek dünyadaki durumlarda, en hızlı uygulamayı isteyeceksiniz Altta yatan LINQ'nun ne kadar havalı olduğuna dair.

İşte yüklü türler arasında yineleme için benim Utils yöntemi. Normal sınıfların yanı sıra arabirimleri de işler ve kendi / üçüncü taraf kod tabanınızda uygulamalar arıyorsanız excludeSystemTypes seçeneği işleri çok hızlandırır.

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

Hoş değil, kabul edeceğim.


2
Sayıcılar, bir denemede / sonunda imha edilmeyen IDisposable uygularlar. Bir foreach veya linq kullanmak daha iyidir.
TamusJRoyce

Neden excludeSystemTypesbirinde iki kez test yapıyorsunuz if?
NetMage

4

Diğer cevaplar genel bir arayüzle çalışmıyordu .

Bunu yaparsanız, sadece typeof (ISomeInterface) yerine typeof (T) yazın.

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

Böylece

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

tüm meclisleri alıyoruz

!x.IsInterface && !x.IsAbstract

arayüzü ve soyut olanları hariç tutmak için kullanılır ve

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

onları bir liste haline getirmek için.


Lütfen çözümünüzün nasıl çalıştığını ve diğer tüm cevaplardan neden daha üstün olduğunu açıklayın.
Lukas Körfer

Üstün ya da alçak değil, diğer cevaplar benim için işe yaramadı ve bunu paylaşmaktan rahatsız oldum.
Antonin GAVREL

Yorumum sadece cevabınızın sadece kod olmasıydı, bu yüzden sizden biraz açıklama eklemenizi istedim.
Lukas Körfer

2

Yapmak istediğinizi yapmanın kolay bir yolu yok (performans açısından).

Yansıma esas olarak montajlar ve türlerle çalışır, böylece montajın tüm türlerini almanız ve doğru arayüz için sorgulamanız gerekir. İşte bir örnek:

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

Bu, Montaj MyAssembly içinde IMyInterface uygulayan tüm türleri alacak


2

Montaj yerini seçerken daha da iyi. Uygulanan tüm arabirimlerinizin aynı Assembly içinde olduğunu biliyorsanız montajların çoğunu filtreleyin.

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

Can Bilgin adlı geliştiriciden



1

Halihazırda birçok geçerli cevap var ama farklı senaryoları göstermek için Type uzantısı ve birim testleri listesi olarak başka bir uygulama eklemek istiyorum:

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

Bu algoritma aşağıdaki senaryoları destekler:

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-kodunda istisnalar var (bu yüzden karmaşık bir uzantı olmadan):

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

Listeyi almak için bazı LINQ'ları kullanabilirsiniz:

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

Ama gerçekten, bu daha okunabilir mi?


6
Çalışırsa daha okunabilir olabilir. Ne yazık ki, nerede yan tümcesiniz, System.Type sınıfının bir örneğinin ISomeInterface'i uygulayıp uygulamadığını görmek için denetliyor, ISomeInterface gerçekten IReflect veya ICustomAttributeProvider değilse, bu durumda her zaman doğru olacaktır.
Joel Mueller

Yukarıdaki Carl Nayak yanıtı nerede yan tümcesini düzeltme cevabına sahiptir: IsAssignableFrom. Bir cevap için kolay hata.
TamusJRoyce
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.