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.