Bagaimana cara mendapatkan tipe T dari anggota kelas umum atau metode?


675

Katakanlah saya memiliki anggota generik di kelas atau metode, jadi:

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

Ketika saya instantiate kelas, Tmenjadi MyTypeObject1, sehingga kelas memiliki properti daftar generik: List<MyTypeObject1>. Hal yang sama berlaku untuk metode generik di kelas non-generik:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

Saya ingin tahu, jenis objek apa yang terdapat dalam daftar kelas saya. Jadi daftar properti yang dipanggil Baratau variabel lokal baz, berisi jenis apa T?

Saya tidak bisa melakukannya Bar[0].GetType(), karena daftar mungkin berisi elemen nol. Bagaimana saya bisa melakukannya?

Jawaban:


695

Jika saya mengerti dengan benar, daftar Anda memiliki parameter tipe yang sama dengan kelas kontainer itu sendiri. Jika ini masalahnya, maka:

Type typeParameterType = typeof(T);

Jika Anda berada dalam situasi beruntung memiliki objectsebagai parameter tipe, lihat jawaban Marc .


4
Lol - ya, sangat benar; Saya berasumsi bahwa OP hanya memiliki object, IList, atau serupa - tapi ini bisa sangat baik menjadi jawaban yang tepat.
— Marc Gravell

32
Saya suka membaca typeof. Jika Anda ingin mengetahui tipe T, gunakan saja typeof(T):)
— demoncodemonkey

2
Saya sebenarnya hanya menggunakan typeof (Type) dan itu berfungsi dengan baik.
— Anton

1
Anda tidak dapat menggunakan typeof () dengan parameter generik.
— Reynevan

2
@Reynevan Tentu saja Anda dapat menggunakan typeof()parameter generik. Apakah Anda punya contoh di mana itu tidak akan berhasil? Atau apakah Anda membingungkan parameter dan referensi tipe?
— Luaan

520

(catatan: Saya berasumsi bahwa yang Anda tahu adalah objectatauIList atau serupa, dan bahwa daftar bisa menjadi semua jenis saat runtime)

Jika Anda tahu itu adalah List<T>, maka:

Type type = abc.GetType().GetGenericArguments()[0];

Pilihan lain adalah melihat pengindeks:

Type type = abc.GetType().GetProperty("Item").PropertyType;

Menggunakan TypeInfo baru:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
Ketik type = abc.GetType (). GetGenericArguments () [0]; ==> Indeks array di luar batas ...
— Patrick Desjardins

28
@Dok: maka itu bukan Daftar <T>
— Marc Gravell

Butuh sesuatu untuk BindingList atau Daftar atau objek apa pun yang memiliki <T>. Apa yang saya lakukan menggunakan BindingListView kustom <T>
— Patrick Desjardins

1
Cobalah dengan BindingList <T>, BindingListView <T> kami mewarisi dari BindingList <T> dan keduanya saya telah mencoba kedua opsi Anda dan tidak berfungsi. Saya mungkin melakukan sesuatu yang salah ... tapi saya pikir solusi ini berfungsi untuk tipe Daftar <T> tetapi tidak untuk tipe daftar lainnya.
— Patrick Desjardins

Ketik type = abc.GetType (). GetProperty ("Item"). PropertyType; kembalikan BindingListView <MyObject> alih-alih MyObject ...
— Patrick Desjardins

49

Dengan metode ekstensi berikut, Anda dapat keluar tanpa refleksi:

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

Atau lebih umum:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

Pemakaian:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

10
Ini hanya berguna jika Anda sudah tahu jenis Twaktu kompilasi. Dalam hal ini, Anda tidak perlu kode sama sekali.
— rekursif

'mengembalikan default (T);'
— fantastory

1
@recursive: Berguna jika Anda bekerja dengan daftar jenis anonim.
— JJJ

Saya melakukan hal yang sama persis sebelum saya membaca jawaban Anda tetapi saya telah memanggil milik sayaItemType
— toddmo

31

Mencoba

list.GetType().GetGenericArguments()

7
Daftar baru <int> () .GetType (). GetGenericArguments () mengembalikan System.Type [1] di sini dengan System.Int32 sebagai entri
— Rauhotz

@ Rauhotz GetGenericArgumentsmetode mengembalikan objek Array Type, yang Anda perlu kemudian menguraikan posisi Tipe Generik yang Anda butuhkan. Seperti Type<TKey, TValue>: Anda harus GetGenericArguments()[0]mendapatkan TKeytipe dan GetGenericArguments()[1]mendapatkan TValuetipe
— GoldBishop

14

Itu bekerja untuk saya. Di mana myList adalah semacam daftar yang tidak diketahui.

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
Saya mendapatkan kesalahan karena memerlukan argumen tipe (yaitu <T>)
— Joseph Humfrey

Joseph dan yang lainnya, untuk menghilangkan kesalahan itu ada di System.Collections.
— user2334883

1
Baris kedua dibutuhkan untukku. A Listsudah merupakan implementasi dari IEnumerable, jadi para pemain sepertinya tidak menambahkan apa-apa. Tapi terima kasih, ini solusi yang bagus.
— pipedreambomb

10

Jika Anda tidak memerlukan variabel Type keseluruhan dan hanya ingin memeriksa tipe Anda dapat dengan mudah membuat variabel temp dan gunakan operator.

T checkType = default(T);

if (checkType is MyClass)
{}

9

Anda dapat menggunakan yang ini untuk jenis kembali daftar umum:

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

8

Pertimbangkan ini: Saya menggunakannya untuk mengekspor 20 daftar yang diketik dengan cara yang sama:

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

1
Ini tidak berfungsi jika T adalah superclass abstrak dari objek yang ditambahkan sebenarnya. Belum lagi, hanya new T();akan melakukan hal yang sama (T)Activator.CreateInstance(typeof(T));. Memang mengharuskan Anda menambahkan where T : new()ke definisi kelas / fungsi, tetapi jika Anda ingin membuat objek, itu harus tetap dilakukan.
— Nyerguds

Juga, Anda menelepon GetTypepada FirstOrDefaultentri mengakibatkan pengecualian referensi null potensial. Jika Anda yakin itu akan mengembalikan setidaknya satu item, mengapa tidak menggunakannya First?
— Mathias Lykkegaard Lorenzen

6

Saya menggunakan metode ekstensi ini untuk mencapai sesuatu yang serupa:

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

Anda menggunakannya seperti ini:

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

Ini bukan yang Anda cari, tetapi sangat membantu dalam menunjukkan teknik yang terlibat.


Hai, terima kasih atas jawaban Anda, dapatkah Anda menambahkan ekstensi untuk "StripStartingWith" juga?
— Cedric Arnould

1
@CedricArnould - Ditambahkan.
— Ken Smith

5

The GetGenericArgument()Metode harus diatur pada Basis Jenis Misalnya Anda (yang kelasnya adalah kelas generikmyClass<T> ). Jika tidak, ia mengembalikan suatu tipe [0]

Contoh:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

Anda bisa mendapatkan jenis "T" dari semua jenis koleksi yang mengimplementasikan IEnumerable <T> dengan yang berikut:

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

Perhatikan bahwa itu tidak mendukung tipe pengumpulan yang mengimplementasikan IEnumerable <T> dua kali, misalnya

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

Saya kira Anda dapat mengembalikan berbagai jenis jika Anda perlu mendukung ini ...

Selain itu, Anda mungkin juga ingin men-cache hasil per jenis koleksi jika Anda sering melakukan ini (misalnya dalam satu lingkaran).


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
Ini muncul untuk menjawab pertanyaan apakah jenisnya adalah jenis daftar-y, tetapi pertanyaannya lebih lanjut tentang bagaimana menentukan parameter tipe generik jenis yang dikenal sebagai Daftar yang sudah diinisialisasi.
— Nathan Tuggy

1

Menggunakan solusi 3dGrabber:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

Jika Anda ingin mengetahui jenis yang mendasari properti, coba ini:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

Beginilah cara saya melakukannya

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

Lalu panggil seperti ini

var item = Activator.CreateInstance(iListType.GetElementType());

ATAU

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

Tipe:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
Ini akan melempar NullReferenceException jika daftar tidak memiliki elemen di dalamnya untuk diuji.
— rossisdead

1
SingleOrDefault()juga melempar InvalidOperationExceptionketika ada dua atau lebih elemen.
— devgeezer

Jawaban ini salah, seperti yang ditunjukkan dengan benar oleh \ @rossisdead dan \ @devgeezer.
— Oliver
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.