Cara menentukan apakah suatu tipe mengimplementasikan antarmuka dengan refleksi C #

562

Apakah refleksi dalam C#menawarkan cara untuk menentukan apakah beberapa System.Typemodel yang diberikan beberapa antarmuka?

public interface IMyInterface {}

public class MyType : IMyInterface {}

// should yield 'true'
typeof(MyType)./* ????? */MODELS_INTERFACE(IMyInterface);
Yippie-Ki-Yay
sumber

Jawaban:

969

Anda punya beberapa pilihan:

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

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

Untuk antarmuka generik, ini sedikit berbeda.

typeof(MyType).GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMyInterface<>))
Jeff
sumber
68
Ingat bahwa typeof (IMyInterface) .IsAssignableFrom (typeof (IMyInterface)) juga benar, yang mungkin memiliki hasil yang tidak terduga pada kode Anda.
Chris Kemp
29
Memang mudah untuk tidak memperhatikan dan mendapatkan argumen untuk IsAssignableFrommundur. Saya akan pergi dengan GetInterfacessekarang: p
Benjamin
12
The IsAssignableFrom(t1)varian sekitar 3x lebih cepat dari GetInterfaces().Contains(t2)rekan dalam kode saya.
Pierre Arnaud
24
@PierreArnaud: IsAssignableFrom akhirnya memanggil GetInterfaces, jadi mungkin tes Anda memeriksa GetInterfaces terlebih dahulu dan IsAssignable setelahnya. Itu karena GetInterfaces menyimpan hasilnya sehingga permintaan pertama lebih mahal
Panos Theof
17
Perubahan kecil pada jawaban @ Kosta. Dengan C # 6 kita dapat melakukan typeof(MyType).GetInterface(nameof(IMyInterface)) != nulluntuk jenis keamanan yang lebih baik dan refactoring.
aholmes
74

Gunakan Type.IsAssignableFrom:

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
Snea
sumber
32
typeof(IMyInterface).IsAssignableFrom(someclass.GetType());

atau

typeof(IMyInterface).IsAssignableFrom(typeof(MyType));
ajma
sumber
34
Jika Anda sudah memiliki instance kelas, pendekatan yang jauh lebih baik hanyalah someclass is IMyInterfacekarena tidak melibatkan biaya refleksi sama sekali. Jadi, meski tidak salah, itu bukan cara yang ideal untuk melakukannya.
James J. Regan IV
1
@ James - Setuju. Bahkan Resharper memberikan saran yang sama.
Angshuman Agarwal
@ JamesJ.ReganIV Anda harus memposting itu sebagai jawaban, saya hampir melewatkan komentar Anda
reggaeguitar
@reggaeguitar, terima kasih, tapi komentarnya tidak menjawab pertanyaan awal. Pertanyaannya meminta solusi Refleksi, saya hanya mengatakan dalam kasus pertama jawaban ini di mana Anda memiliki contoh objek refleksi bukan solusi ideal.
James J. Regan IV
1
@ JamesJ.ReganIV Sebenarnya, ismemeriksa di kedua arah hierarki warisan sedangkan IsAssignableFromhanya memeriksa ke atas. Juga, jika Anda memiliki instance objek, Anda harus memanggil IsInstanceOfType(yang juga hanya melihat ke atas).
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;
}

Saya pikir ini adalah rilis yang benar, karena tiga alasan:

  1. Ia menggunakan GetInterfaces dan bukan IsAssignableFrom, lebih cepat karena IsAssignableFrom akhirnya setelah beberapa pemeriksaan memanggil GetInterfaces.
  2. Itu beralih ke array lokal, sehingga tidak akan ada batas pemeriksaan.
  3. Ia menggunakan operator == yang didefinisikan untuk Tipe, jadi mungkin lebih aman daripada metode Persamaan (yang panggilan Contains, akhirnya akan gunakan).
Panos Theof
sumber
10
+1 untuk konten, saya benci ruang di sekitar paren dan kawat gigi Mesir. Seluruh metode juga dapat ditulis sebagai: return type.GetInterfaces (). Any (t => t == ifaceType);
reggaeguitar
1
Type.IsAssignableFrom () internaly bertindak persis seperti kode Anda
devi
1
Juga mengapa tidak mengetikkan. GetInterfaces (). Berisi (ifaceType) yang tidak menggunakan LINQ.
9

Saya baru saja melakukannya:

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

Saya berharap saya bisa mengatakan where I : interface, tetapi interfacebukan opsi kendala parameter generik. classsedekat yang didapatnya.

Pemakaian:

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

Saya hanya mengatakan Implementskarena itu lebih intuitif. Saya selalu IsAssignableFromjungkir balik.

toddmo
sumber
Anda bisa lakukan return typeof(I).IsInterface && typeof(I).IsAssignableFrom(source);untuk mengembalikan false pada setiap penggunaan metode yang 'salah', yaitu; menggunakannya dengan tipe kelas dan bukan tipe antarmuka, atau melemparkan pengecualian jika tipe-parameter bukan antarmuka. Meskipun Anda bisa berargumen bahwa kelas turunan 'mengimplementasikan' itu orang tua ...
Sindri Jóelsson
7

Memodifikasi jawaban Jeff untuk kinerja optimal (berkat tes kinerja oleh Pierre Arnaud):

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

Untuk menemukan semua jenis yang mengimplementasikan antarmuka dalam yang diberikan Assembly:

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

Seperti yang telah disebutkan orang lain: Benjamin 10 Apr '13 pada 22:21 "

Memang mudah untuk tidak memperhatikan dan mendapatkan argumen untuk IsAssignableFrom mundur. Saya akan pergi dengan GetInterfaces sekarang: p -

Nah, cara lain adalah menciptakan metode ekstensi pendek yang memenuhi, sampai batas tertentu, cara berpikir "paling umum" (dan sepakat bahwa ini adalah pilihan pribadi yang sangat sedikit untuk menjadikannya sedikit "lebih alami" berdasarkan preferensi seseorang. ):

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

Dan mengapa tidak menjadi sedikit lebih generik (yah tidak yakin apakah itu benar-benar semenarik itu, saya kira saya hanya melewatkan sedikit gula 'sintaksis'):

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

Saya pikir itu mungkin jauh lebih alami seperti itu, tetapi sekali lagi hanya masalah pendapat yang sangat pribadi:

var isTrue = michelleType.IsAssignableTo<IMaBelle>();
Kerry Perret
sumber
4
Apakah ada alasan Anda tidak langsung menerapkannya dalam metode ekstensi? Maksud saya yakin ini memungkinkan Anda menyebutnya dua arah, tetapi mengapa Anda harus melakukan itu?
Mark A. Donohoe
@MarqueIV maaf untuk membalas Anda hampir 2 tahun terlambat, yah saya rasa itu sudah menjadi kebiasaan lama dulu untuk membungkus metode pembantu dalam metode ekstensi untuk menghindari kode berulang, akan mengedit jawaban saya :)
Kerry Perret
1
@MarqueIV selesai dan mengubah kebiasaan buruk saya yang lain karena tidak menggunakan alias, yaitu Boolean=> bool(Saya tidak mengapa saya dulu memiliki beberapa aturan pengkodean "mewah" yang ketat ketika saya masih muda).
Kerry Perret
3

Jika Anda memiliki tipe atau instance, Anda dapat dengan mudah memeriksa apakah mereka mendukung antarmuka tertentu.

Untuk menguji apakah suatu objek mengimplementasikan antarmuka tertentu:

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

Untuk menguji apakah suatu tipe mengimplementasikan antarmuka tertentu:

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

Jika Anda mendapatkan objek generik dan ingin melakukan pemeran serta memeriksa apakah antarmuka yang Anda gunakan untuk mengimplementasikan kode tersebut adalah:

 var myCastedObject = myObject as IMyInterface;

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

sumber
2

IsAssignableFromsekarang dipindahkan ke TypeInfo:

typeof(ISMSRequest).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo());
codeputer
sumber
1

Siapa pun yang mencari ini mungkin menemukan metode ekstensi berikut berguna:

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

tes xunit:

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

bagaimana dengan

if(MyType as IMyInterface != null)

?

Pingi
sumber
4
Ini jelas ketika saya memiliki sebuah contoh. Tidak berguna ketika saya memiliki Tipe dari refleksi
edc65
0

Bagaimana dengan

typeof(IWhatever).GetTypeInfo().IsInterface
LaWi
sumber
0

Jawaban yang benar adalah

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

Namun,

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

mungkin mengembalikan hasil yang salah, seperti yang ditunjukkan kode berikut dengan string dan IConvertible:

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

Hasil:

 isConvertibleDirect: True
 isConvertibleTypeAssignable: False
 isConvertibleHasInterface: True
EricBDev
sumber
4
Seperti yang Anda lihat dalam jawaban yang diterima, Anda menukar jenis penggunaan IsAssignableFrom. Persis seperti yang diperingatkan Benjamin dan Ehouarn.
VV5198722
0

Perhatikan bahwa jika Anda memiliki antarmuka generik IMyInterface<T>maka ini akan selalu kembali false:

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

Ini juga tidak bekerja:

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

Namun, jika MyTypemengimplementasikan IMyInterface<MyType>ini berhasil dan mengembalikan true:

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

Namun, Anda mungkin tidak akan tahu parameter tipe Tsaat runtime . Solusi yang agak rumit adalah:

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

Solusi Jeff sedikit kurang rapi:

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

Berikut adalah metode ekstensi Typeyang berfungsi untuk kasus apa pun:

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

(Perhatikan bahwa di atas menggunakan linq, yang mungkin lebih lambat dari satu loop.)

Anda kemudian dapat melakukan:

   typeof(MyType).IsImplementing(IMyInterface<>)
Diego
sumber