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

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

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


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

atau

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

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).

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.


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

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

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
    }

2

IsAssignableFromsekarang dipindahkan ke TypeInfo:

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

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

0

bagaimana dengan

if(MyType as IMyInterface != null)

?


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

0

Bagaimana dengan

typeof(IWhatever).GetTypeInfo().IsInterface

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

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<>)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.