Menguji apakah objek adalah tipe generik dalam C #


134

Saya ingin melakukan tes jika suatu objek adalah tipe generik. Saya sudah mencoba yang berikut ini tanpa hasil:

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

Apa yang saya lakukan salah dan bagaimana saya melakukan tes ini?

Jawaban:


201

Jika Anda ingin memeriksa apakah ini merupakan instance dari tipe generik:

return list.GetType().IsGenericType;

Jika Anda ingin memeriksa apakah itu generik List<T>:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

Seperti yang ditunjukkan oleh Jon, ini memeriksa kesetaraan tipe persisnya. Kembali falsetidak selalu berarti list is List<T>pengembalian false(yaitu objek tidak dapat ditugaskan ke List<T>variabel).


9
Itu tidak akan mendeteksi subtipe. Lihat jawaban saya. Ini juga jauh lebih sulit untuk antarmuka :(
Jon Skeet

1
Panggilan ke GetGenericTypeDefinition akan dibuang jika itu bukan tipe generik. Pastikan Anda memeriksa itu dulu.
Kilhoffer

85

Saya berasumsi bahwa Anda tidak hanya ingin tahu apakah tipe itu generik, tetapi jika suatu objek adalah instance dari tipe generik tertentu, tanpa mengetahui argumen tipe.

Sayangnya, ini tidak terlalu sederhana. Tidak terlalu buruk jika tipe generiknya adalah kelas (seperti dalam kasus ini) tetapi lebih sulit untuk antarmuka. Berikut kode untuk kelas:

using System;
using System.Collections.Generic;
using System.Reflection;

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

EDIT: Seperti disebutkan dalam komentar, ini dapat berfungsi untuk antarmuka:

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

Saya memiliki kecurigaan yang menyelinap mungkin ada beberapa kasus tepi canggung di sekitar ini, tetapi saya tidak dapat menemukan satu itu gagal untuk saat ini.


2
Baru saja menemukan masalah dengan ini. Hanya turun satu baris warisan. Jika, di sepanjang jalan, Anda memiliki basis dengan kelas dasar dan antarmuka yang Anda cari, ini hanya menyusuri jalur kelas saja.
Groxx

1
@Groxx: Benar. Saya baru saja melihat bahwa saya menyebutkannya dalam jawaban: "Tidak terlalu buruk jika tipe generik adalah kelas (seperti dalam kasus ini) tetapi lebih sulit untuk antarmuka. Berikut kode untuk kelas"
Jon Skeet

1
Bagaimana jika Anda tidak memiliki cara untuk mengetahui <T>? Seperti, mungkin int, atau string, tetapi Anda tidak tahu itu. Ini menghasilkan, kelihatannya, false negative ... jadi Anda tidak memiliki T untuk digunakan, Anda hanya melihat melalui properti dari beberapa objek dan satu adalah daftar. Bagaimana Anda tahu itu daftar sehingga Anda bisa melepaskannya? Maksud saya, Anda tidak memiliki T di mana pun atau tipe untuk digunakan. Anda dapat menebak setiap jenis (apakah itu Daftar <int>? Apakah itu Daftar <string>?) Tetapi yang ingin Anda ketahui adalah APAKAH INI DAFTAR AA? Pertanyaan itu sepertinya sulit dijawab.

@RiverC: Ya, kau benar - itu adalah cukup sulit untuk jawaban, karena berbagai alasan. Jika Anda hanya berbicara tentang kelas, itu tidak terlalu buruk ... Anda dapat terus berjalan di pohon warisan dan melihat apakah Anda menekan List<T>dalam beberapa bentuk atau lainnya. Jika Anda menyertakan antarmuka, itu sangat rumit.
Jon Skeet

3
tidak bisakah Anda mengganti loop IsInstanceOfGenericTypedengan panggilan ke IsAssignableFromalih-alih operator kesetaraan ( ==)?
slawekwin

7

Anda dapat menggunakan kode pendek menggunakan dinamis meskipun ini mungkin lebih lambat dari refleksi murni:

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();

7

Ini adalah dua metode ekstensi favorit saya yang mencakup sebagian besar kasus pemeriksaan tipe generik:

Bekerja dengan:

  • Beberapa antarmuka (umum)
  • Beberapa kelas dasar (generik)
  • Memiliki kelebihan yang akan 'keluar' dari tipe generik spesifik jika itu mengembalikan true (lihat unit test untuk sampel):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }

Inilah tes untuk menunjukkan fungsionalitas (dasar):

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }

0
return list.GetType().IsGenericType;

3
Itu benar untuk pertanyaan yang berbeda. Untuk pertanyaan ini, itu tidak benar, karena hanya membahas (secara signifikan kurang dari) setengah dari masalah.
Groxx

1
Jawaban Stan R sebenarnya menjawab pertanyaan yang diajukan, tetapi apa yang sebenarnya dimaksudkan OP adalah "Menguji apakah objek adalah tipe generik tertentu dalam C #", yang jawabannya memang tidak lengkap.
yoyo

orang-orang memilih saya karena saya menjawab pertanyaan dalam konteks "adalah" tipe generik daripada "adalah tipe" generik. Bahasa Inggris adalah bahasa kedua saya dan nuansa bahasa seperti itu sering berlalu begitu saja, untuk pertahanan saya OP tidak secara khusus meminta untuk menguji terhadap jenis tertentu dan dalam judulnya menanyakan "adalah dari" jenis umum ... tidak yakin mengapa saya pantas menerima downvotes untuk sebuah pertanyaan yang ambigu.
Stan R.

2
Sekarang Anda tahu itu dan Anda dapat meningkatkan jawaban Anda menjadi lebih spesifik dan benar.
Peter Ivan
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.