apakah ada cara untuk mengambil tipe T
dari IEnumerable<T>
melalui refleksi?
misalnya
saya punya IEnumerable<Child>
info variabel ; Saya ingin mendapatkan kembali tipe Child melalui refleksi
apakah ada cara untuk mengambil tipe T
dari IEnumerable<T>
melalui refleksi?
misalnya
saya punya IEnumerable<Child>
info variabel ; Saya ingin mendapatkan kembali tipe Child melalui refleksi
Jawaban:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Demikian,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
cetakan System.String
.
Lihat MSDN untuk Type.GetGenericArguments
.
Sunting: Saya yakin ini akan mengatasi masalah di komentar:
// returns an enumeration of T where o : IEnumerable<T>
public IEnumerable<Type> GetGenericIEnumerables(object o) {
return o.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0]);
}
Beberapa objek mengimplementasikan lebih dari satu generik IEnumerable
sehingga perlu mengembalikan enumerasi mereka.
Sunting: Meskipun, saya harus mengatakan, itu adalah ide yang buruk bagi kelas untuk diterapkan IEnumerable<T>
lebih dari satu T
.
Saya baru saja membuat metode ekstensi. Ini bekerja dengan semua yang saya lemparkan.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
Saya memiliki masalah serupa. Jawaban yang dipilih berfungsi untuk contoh aktual. Dalam kasus saya, saya hanya memiliki tipe (dari a PropertyInfo
).
Jawaban yang dipilih gagal ketika tipe itu sendiri typeof(IEnumerable<T>)
bukan merupakan implementasi dari IEnumerable<T>
.
Untuk kasus ini pekerjaan berikut:
public static Type GetAnyElementType(Type type)
{
// Type is Array
// short-circuit if you expect lots of arrays
if (type.IsArray)
return type.GetElementType();
// type is IEnumerable<T>;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>))
return type.GetGenericArguments()[0];
// type implements/extends IEnumerable<T>;
var enumType = type.GetInterfaces()
.Where(t => t.IsGenericType &&
t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GenericTypeArguments[0]).FirstOrDefault();
return enumType ?? type;
}
Type.GenericTypeArguments
- hanya untuk dotNet FrameWork versi> = 4.5. Jika tidak - gunakan Type.GetGenericArguments
saja.
Jika Anda tahu IEnumerable<T>
(melalui generik), maka typeof(T)
seharusnya berfungsi. Jika tidak (untuk object
, atau non-generik IEnumerable
), periksa antarmuka yang diterapkan:
object obj = new string[] { "abc", "def" };
Type type = null;
foreach (Type iType in obj.GetType().GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition()
== typeof(IEnumerable<>))
{
type = iType.GetGenericArguments()[0];
break;
}
}
if (type != null) Console.WriteLine(type);
Type type
parameter daripada object obj
parameter: Anda tidak bisa hanya mengganti obj.GetType()
dengan type
karena jika Anda meneruskan typeof(IEnumerable<T>)
Anda tidak mendapatkan apa-apa. Untuk mengatasi ini, uji type
dirinya sendiri untuk melihat apakah itu generik IEnumerable<>
dan kemudian antarmukanya.
Terima kasih banyak atas pembahasannya. Saya menggunakannya sebagai dasar untuk solusi di bawah ini, yang berfungsi dengan baik untuk semua kasus yang menarik bagi saya (IEnumerable, kelas turunan, dll). Saya pikir saya harus berbagi di sini jika ada yang membutuhkannya juga:
Type GetItemType(object someCollection)
{
var type = someCollection.GetType();
var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
return ienum != null
? ienum.GetGenericArguments()[0]
: null;
}
someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Gunakan saja typeof(T)
EDIT: Atau gunakan .GetType (). GetGenericParameter () pada objek yang dibuat jika Anda tidak memiliki T.
Alternatif untuk situasi yang lebih sederhana di mana itu akan menjadi IEnumerable<T>
atau T
- gunakan catatan GenericTypeArguments
alih-alih GetGenericArguments()
.
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Ini adalah perbaikan pada solusi Eli Algranti karena ia juga akan berfungsi di mana IEnumerable<>
jenisnya berada di tingkat mana pun dalam pohon warisan.
Solusi ini akan mendapatkan tipe elemen dari apapun Type
. Jika tipe bukan IEnumerable<>
, itu akan mengembalikan tipe yang diteruskan. Untuk objek, gunakan GetType
. Untuk jenis, gunakan typeof
, lalu panggil metode ekstensi ini pada hasil.
public static Type GetGenericElementType(this Type type)
{
// Short-circuit for Array types
if (typeof(Array).IsAssignableFrom(type))
{
return type.GetElementType();
}
while (true)
{
// Type is IEnumerable<T>
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
return type.GetGenericArguments().First();
}
// Type implements/extends IEnumerable<T>
Type elementType = (from subType in type.GetInterfaces()
let retType = subType.GetGenericElementType()
where retType != subType
select retType).FirstOrDefault();
if (elementType != null)
{
return elementType;
}
if (type.BaseType == null)
{
return type;
}
type = type.BaseType;
}
}
Saya tahu ini agak lama, tetapi saya yakin metode ini akan mencakup semua masalah dan tantangan yang disebutkan di komentar. Penghargaan untuk Eli Algranti karena telah menginspirasi pekerjaan saya.
/// <summary>Finds the type of the element of a type. Returns null if this type does not enumerate.</summary>
/// <param name="type">The type to check.</param>
/// <returns>The element type, if found; otherwise, <see langword="null"/>.</returns>
public static Type FindElementType(this Type type)
{
if (type.IsArray)
return type.GetElementType();
// type is IEnumerable<T>;
if (ImplIEnumT(type))
return type.GetGenericArguments().First();
// type implements/extends IEnumerable<T>;
var enumType = type.GetInterfaces().Where(ImplIEnumT).Select(t => t.GetGenericArguments().First()).FirstOrDefault();
if (enumType != null)
return enumType;
// type is IEnumerable
if (IsIEnum(type) || type.GetInterfaces().Any(IsIEnum))
return typeof(object);
return null;
bool IsIEnum(Type t) => t == typeof(System.Collections.IEnumerable);
bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
public static Type GetInnerGenericType(this Type type)
{
// Attempt to get the inner generic type
Type innerType = type.GetGenericArguments().FirstOrDefault();
// Recursively call this function until no inner type is found
return innerType is null ? type : innerType.GetInnerGenericType();
}
Ini adalah fungsi rekursif yang akan masuk ke dalam daftar tipe generik terlebih dahulu sampai mendapatkan definisi tipe konkret tanpa tipe generik dalam.
Saya menguji metode ini dengan tipe ini:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
yang harus dikembalikan IActionResult
typeof(IEnumerable<Foo>)
. akan mengembalikan argumen umum pertama - dalam kasus ini .GetGenericArguments()
[0]
typeof(Foo)
Ini versi ekspresi kueri Linq saya yang tidak dapat dibaca ..
public static Type GetEnumerableType(this Type t) {
return !typeof(IEnumerable).IsAssignableFrom(t) ? null : (
from it in (new[] { t }).Concat(t.GetInterfaces())
where it.IsGenericType
where typeof(IEnumerable<>)==it.GetGenericTypeDefinition()
from x in it.GetGenericArguments() // x represents the unknown
let b = it.IsConstructedGenericType // b stand for boolean
select b ? x : x.BaseType).FirstOrDefault()??typeof(object);
}
Perhatikan bahwa metode ini juga memperhitungkan non-generik IEnumerable
, ia mengembalikan object
dalam kasus ini, karena mengambil Type
contoh konkret daripada sebagai argumen. Ngomong-ngomong, karena x mewakili hal yang tidak diketahui , menurut saya video ini menarik , meskipun tidak relevan ..