Bir türün C # yansımasıyla bir arabirim uygulayıp uygulamadığı nasıl belirlenir


562

Does yansıma içinde C#teklif şekilde bazı verilmiş olmadığını belirlemek için System.Typebazı arayüz tipi modellerinde?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);

Yanıtlar:


969

Birkaç seçeneğiniz var:

  1. typeof(IMyInterface).IsAssignableFrom(typeof(MyType))

  2. typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface))

Genel bir arayüz için biraz farklı.

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))

68
Kodunuzda beklenmeyen bir sonuç doğurabilecek typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) öğesinin de doğru olduğunu unutmayın.
Chris Kemp

29
Dikkat etmemek ve IsAssignableFromgeriye dönük argümanları almak kolaydı . GetInterfacesŞimdi gideceğim : p
Benjamin

12
IsAssignableFrom(t1)Varyant daha hızlı 3x hakkındadır GetInterfaces().Contains(t2)benim kodunda muadili.
Pierre Arnaud

24
@PierreArnaud: IsAssignableFrom sonunda GetInterfaces'ı çağırır, bu nedenle testiniz önce GetInterfaces'ı ve sonra IsAssignable'ı kontrol etti. Bunun nedeni GetInterfaces'in sonuçlarını önbelleğe
almasıdır

17
@ Kosta'nın cevabında küçük bir değişiklik. C # 6 ile typeof(MyType).GetInterface(nameof(IMyInterface)) != nulldaha iyi tip güvenlik ve yeniden düzenleme için yapabiliriz.
aholmes


32
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

veya

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));

34
Sınıfın bir örneğine zaten sahipseniz, çok daha iyi bir yaklaşım basitçe someclass is IMyInterfacebu, yansıtma maliyetini içermez. Yani, yanlış olmasa da, bunu yapmak için ideal bir yol değildir.
James J. Regan IV

1
@James - Katılıyorum. Resharper bile aynı öneriyi veriyor.
Angshuman Agarwal

@ JamesJ.ReganIV bunu bir cevap olarak göndermelisiniz, neredeyse yorumunuzu kaçırdım
reggaeguitar

@reggaeguitar, teşekkürler, ancak yorum orijinal soruya cevap vermiyor. Soru Yansıma çözümünü soruyor, sadece bu cevabın nesne yansımasının bir örneğine sahip olduğunuz ilk durumda ideal çözüm olmadığını söylüyorum.
James J. Regan IV

1
@ JamesJ.ReganIV Aslında, ismiras hiyerarşisinin her iki yönünde IsAssignableFromkontrol ederken, sadece yukarı doğru kontrol eder. Ayrıca, bir nesnenin örneğiniz varsa, çağırmalısınız IsInstanceOfType(bu yalnızca yukarı bakar).
Sellorio

13
public static bool ImplementsInterface(this Type type, Type ifaceType) 
{
    Type[] intf = type.GetInterfaces();
    for(int i = 0; i < intf.Length; i++) 
    {
        if(intf[ i ] == ifaceType) 
        {
            return true;
        }
    }
    return false;
}

Üç nedenden dolayı bu doğru sürüm olduğunu düşünüyorum:

  1. IsAssignableFrom yerine GetInterfaces kullanır, IsAssignableFrom, birkaç kontrol GetInterfaces çağırdıktan sonra sonunda daha hızlıdır.
  2. Yerel dizi üzerinden yinelenir, bu nedenle sınır kontrolü yapılmaz.
  3. Tür için tanımlanan == işlecini kullanır, bu nedenle muhtemelen Eşittir yönteminden daha güvenlidir (İçerir çağrı içerir, sonunda kullanır).

10
+1 içerik için, ben parens ve Mısır diş telleri çevresindeki boşluklardan nefret ediyorum. Ayrıca tüm yöntem şu şekilde yazılabilir: return type.GetInterfaces (). Herhangi biri (t => t == ifaceType);
reggaeguitar

1
Type.IsAssignableFrom () dahili olarak tam olarak kodunuz gibi davranır
devi

1
Ayrıca neden type.GetInterfaces (). LINQ kullanmayan (ifaceType) içerir.

9

Az önce yaptım:

public static bool Implements<I>(this Type source) where I : class
{
  return typeof(I).IsAssignableFrom(source);
}

Keşke söyleyebilirdim where I : interface, ama interfacegenel bir parametre kısıtlama seçeneği değil. classolabildiğince yakın.

Kullanımı:

if(MyType.Implements<IInitializable>())
  MyCollection.Initialize();

Sadece dedim Implementsçünkü bu daha sezgisel. Her zaman IsAssignableFromparmak arası terlik alıyorum .


Sen yapabileceğini return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);olduğunu, yöntemin herhangi bir 'yanlış' kullanımlarda return false için; bunu arayüz türü yerine sınıf türüyle kullanmak, alternatif olarak type parametresi bir arayüz değilse bir istisna atar. Türetilmiş bir sınıfın ebeveyn olduğunu 'uyguladığını' iddia edebilirsiniz ...
Sindri Jóelsson

7

Jeff'in optimum performans için cevabını değiştirmek (Pierre Arnaud'un performans testi sayesinde):

var type = typeof(MyType);
var implementsInterface = typeof(IMyInterface).IsAssignableFrom(type) && type.IsClass;

Belirli bir arabirimde arabirim uygulayan tüm türleri bulmak için Assembly:

var implementations = typeof(TypeInTargetAssembly).Assembly.GetTypes()
                          .Where(t => typeof(IMyInterface).IsAssignableFrom(t) && t.IsClass);

7

Başka birinin de belirttiği gibi: Benjamin 10 '13, 22:21 "

Dikkat etmemek ve IsAssignableFrom için argümanları geri almak kolay değildi. Şimdi GetInterfaces ile gideceğim: p -

Bir başka yol, sadece bir dereceye kadar "en olağan" düşünme tarzını karşılayan kısa bir uzatma yöntemi oluşturmaktır (ve bunun, tercihlerine göre biraz "daha doğal" hale getirilmesi için çok az kişisel bir seçim olduğu konusunda anlaştı. ):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }
}

Ve neden biraz daha genel gitmiyorum (gerçekten bu kadar ilginç olup olmadığından emin değilim, iyi bir 'sözdizimi' şekeri daha geçirdiğimi varsayıyorum):

public static class TypeExtensions
{
    public static bool IsAssignableTo(this Type type, Type assignableType)
    {
        return assignableType.IsAssignableFrom(type);
    }

    public static bool IsAssignableTo<TAssignable>(this Type type)
    {
        return IsAssignableTo(type, typeof(TAssignable));
    }
}

Bu şekilde çok daha doğal olabileceğini düşünüyorum, ama bir kez daha sadece kişisel görüşler meselesi:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();

4
Uygulamayı doğrudan uzantı yöntemine koymamanızın bir nedeni var mı? Demek istediğim, bu her iki şekilde de aramanıza izin veriyor, ama neden bunu yapmanız gerekecek?
Mark A. Donohoe

@MarqueIV size neredeyse 2 yıl geç geri döndüğüm için üzgünüm, iyi bir eski kötü alışkanlık oldu sanırım o zaman tekrar kodunu önlemek için yardımcı yöntemde yardımcı yöntemi sarmak için cevabımı düzenleyecek :)
Kerry Perret

1
@MarqueIV artı diğer adı kullanmama gibi diğer kötü alışkanlığımı değiştirdi, yani Boolean=> bool(neden ben gençken bazı sıkı "fantezi" kodlama kurallarına sahip olduğumu anlamıyorum).
Kerry Perret

3

Bir türünüz veya örneğiniz varsa, belirli bir arabirimi destekleyip desteklemediklerini kolayca kontrol edebilirsiniz.

Bir nesnenin belirli bir arabirimi uygulayıp uygulamadığını test etmek için:

if(myObject is IMyInterface) {
  // object myObject implements IMyInterface
}

Bir türün belirli bir arabirimi uygulayıp uygulamadığını test etmek için:

if(typeof(IMyInterface).IsAssignableFrom(typeof(MyType))) {
  // type MyType implements IMyInterface
}

Genel bir nesneniz varsa ve yayın yaptığınız arayüzün uygulanıp uygulanmadığını kontrol etmenin yanı sıra bir döküm yapmak istiyorsanız:

 var myCastedObject = myObject as IMyInterface;

    if(myCastedObject != null) {
      // object myObject implements IMyInterface
    }

2

IsAssignableFromşuraya taşındı TypeInfo:

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());

1

Bunu arayan herkes aşağıdaki uzantı yöntemini yararlı bulabilir:

public static class TypeExtensions
{
    public static bool ImplementsInterface(this Type type, Type @interface)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }

        if (@interface == null)
        {
            throw new ArgumentNullException(nameof(@interface));
        }

        var interfaces = type.GetInterfaces();
        if (@interface.IsGenericTypeDefinition)
        {
            foreach (var item in interfaces)
            {
                if (item.IsConstructedGenericType && item.GetGenericTypeDefinition() == @interface)
                {
                    return true;
                }
            }
        }
        else
        {
            foreach (var item in interfaces)
            {
                if (item == @interface)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

xunit testleri:

public class TypeExtensionTests
{
    [Theory]
    [InlineData(typeof(string), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<int>), false)]
    [InlineData(typeof(List<>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<>), true)]
    [InlineData(typeof(List<int>), typeof(IList<int>), true)]
    [InlineData(typeof(List<int>), typeof(IList<string>), false)]
    public void ValidateTypeImplementsInterface(Type type, Type @interface, bool expect)
    {
        var output = type.ImplementsInterface(@interface);
        Assert.Equal(expect, output);
    }
}

0

ne dersin

if(MyType as IMyInterface != null)

?


4
Bir örneğim olduğunda bu çok açık. Yansımanın bir türü olduğunda yararlı değil
edc65

0

Ne dersin

typeof(IWhatever).GetTypeInfo().IsInterface

0

Doğru cevap

typeof(MyType).GetInterface(nameof(IMyInterface)) != null;

Ancak,

typeof(MyType).IsAssignableFrom(typeof(IMyInterface));

aşağıdaki kod string ve IConvertible ile gösterildiği gibi yanlış bir sonuç döndürebilir:

    static void TestIConvertible()
    {
        string test = "test";
        Type stringType = typeof(string); // or test.GetType();

        bool isConvertibleDirect = test is IConvertible;
        bool isConvertibleTypeAssignable = stringType.IsAssignableFrom(typeof(IConvertible));
        bool isConvertibleHasInterface = stringType.GetInterface(nameof(IConvertible)) != null;

        Console.WriteLine($"isConvertibleDirect: {isConvertibleDirect}");
        Console.WriteLine($"isConvertibleTypeAssignable: {isConvertibleTypeAssignable}");
        Console.WriteLine($"isConvertibleHasInterface: {isConvertibleHasInterface}");
    }

Sonuçlar:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True

4
Kabul edilen cevapta görebileceğiniz gibi, kullanım türlerini değiştirdiniz IsAssignableFrom. Tıpkı Benjamin ve Ehouarn'ın uyardığı gibi.
VV5198722

0

Genel bir arayüzünüz IMyInterface<T>varsa, bunun her zaman geri döneceğini unutmayın false:

  typeof(IMyInterface<>).IsAssignableFrom(typeof(MyType)) /* ALWAYS FALSE */

Bu da çalışmaz:

  typeof(MyType).GetInterfaces().Contains(typeof(IMyInterface<>))  /* ALWAYS FALSE */

Ancak, eğer MyTypeuygular IMyInterface<MyType>bu eserlerin ve iadeler true:

  typeof(IMyInterface<MyType>).IsAssignableFrom(typeof(MyType))

Ancak, Tçalışma zamanında type parametresini bilemezsiniz . Biraz hileli bir çözüm:

  typeof(MyType).GetInterfaces()
                .Any(x=>x.Name == typeof(IMyInterface<>).Name)

Jeff'in çözümü biraz daha kibirli:

  typeof(MyType).GetInterfaces()
         .Any(i => i.IsGenericType 
             && i.GetGenericTypeDefinition() == typeof(IMyInterface<>));

İşte Typeher durumda çalışan bir uzantı yöntemi :

public static class TypeExtensions
{
    public static bool IsImplementing(this Type type, Type someInterface)
    {
        return type.GetInterfaces()
             .Any(i => i == someInterface 
                 || i.IsGenericType 
                    && i.GetGenericTypeDefinition() == someInterface);
    }
}

(Yukarıda, muhtemelen bir döngüden daha yavaş olan linq kullandığını unutmayın.)

Daha sonra şunları yapabilirsiniz:

   typeof(MyType).IsImplementing(IMyInterface<>)
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.