มีวิธีดึงข้อมูลประเภทT
จากการIEnumerable<T>
สะท้อนหรือไม่?
เช่น
ฉันมีIEnumerable<Child>
ข้อมูลตัวแปร ฉันต้องการดึงข้อมูลประเภทของเด็กผ่านการไตร่ตรอง
มีวิธีดึงข้อมูลประเภทT
จากการIEnumerable<T>
สะท้อนหรือไม่?
เช่น
ฉันมีIEnumerable<Child>
ข้อมูลตัวแปร ฉันต้องการดึงข้อมูลประเภทของเด็กผ่านการไตร่ตรอง
คำตอบ:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
ดังนั้น
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
System.String
พิมพ์
ดูMSDNสำหรับType.GetGenericArguments
.
แก้ไข:ฉันเชื่อว่าสิ่งนี้จะช่วยแก้ปัญหาในความคิดเห็น:
// 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]);
}
อ็อบเจ็กต์บางตัวใช้มากกว่าหนึ่งทั่วไปIEnumerable
ดังนั้นจึงจำเป็นต้องส่งคืนการแจงนับ
แก้ไข:แม้ว่าฉันต้องบอกว่ามันเป็นความคิดที่แย่มากสำหรับชั้นเรียนที่จะนำไปใช้IEnumerable<T>
มากกว่าหนึ่งT
ชั้น
ฉันแค่สร้างวิธีการขยาย สิ่งนี้ใช้ได้กับทุกสิ่งที่ฉันโยนไป
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
ฉันมีปัญหาที่คล้ายกัน คำตอบที่เลือกใช้ได้กับอินสแตนซ์จริง ในกรณีของฉันฉันมีเพียงประเภท (จาก a PropertyInfo
)
คำตอบที่เลือกล้มเหลวเมื่อชนิดที่ตัวเองไม่ได้การดำเนินการของtypeof(IEnumerable<T>)
IEnumerable<T>
สำหรับกรณีนี้การทำงานดังต่อไปนี้:
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
- สำหรับเวอร์ชัน dotNet FrameWork เท่านั้น> = 4.5 มิฉะนั้น - ใช้Type.GetGenericArguments
แทน
หากคุณรู้จักIEnumerable<T>
(ผ่านทางยาสามัญ) ก็typeof(T)
ควรใช้งานได้ มิฉะนั้น (สำหรับobject
หรือไม่ใช่แบบทั่วไปIEnumerable
) ให้ตรวจสอบอินเทอร์เฟซที่ใช้งาน:
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
พารามิเตอร์แทนที่จะเป็นobject obj
พารามิเตอร์: คุณไม่สามารถแทนที่obj.GetType()
ด้วยtype
เพราะถ้าคุณส่งผ่านtypeof(IEnumerable<T>)
คุณจะไม่ได้อะไรเลย เพื่อให้ได้รอบนี้ทดสอบtype
ตัวเองเพื่อดูว่าเป็นIEnumerable<>
อินเทอร์เฟซทั่วไปหรือไม่
ขอบคุณมากสำหรับการสนทนา ฉันใช้มันเป็นพื้นฐานสำหรับวิธีแก้ปัญหาด้านล่างซึ่งใช้ได้ดีกับทุกกรณีที่ฉันสนใจ (IEnumerable คลาสที่ได้รับ ฯลฯ ) คิดว่าฉันควรแบ่งปันที่นี่เผื่อว่าใครต้องการมันด้วย:
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()
ทางเลือกสำหรับสถานการณ์ที่เรียบง่ายที่เป็นอย่างใดอย่างหนึ่งจะเป็นIEnumerable<T>
หรือT
- ใช้บันทึกการแทนGenericTypeArguments
GetGenericArguments()
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
นี่เป็นการปรับปรุงโซลูชันของ Eli Algranti ซึ่งจะใช้งานได้โดยที่IEnumerable<>
ประเภทอยู่ในระดับใดก็ได้ในแผนผังการสืบทอด
Type
การแก้ปัญหานี้จะได้รับชนิดธาตุจากส่วนใด ถ้าชนิดไม่ได้เป็นIEnumerable<>
ก็จะกลับชนิดผ่าน. GetType
สำหรับวัตถุที่ใช้ สำหรับประเภทให้ใช้typeof
จากนั้นเรียกวิธีการขยายนี้กับผลลัพธ์
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;
}
}
ฉันรู้ว่านี่เก่าไปหน่อย แต่ฉันเชื่อว่าวิธีนี้จะครอบคลุมปัญหาและความท้าทายทั้งหมดที่ระบุไว้ในความคิดเห็น ให้เครดิตกับ Eli Algranti สำหรับแรงบันดาลใจในการทำงานของฉัน
/// <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();
}
นี่คือฟังก์ชันแบบวนซ้ำซึ่งจะลงลึกในรายการประเภททั่วไปก่อนจนกว่าจะได้คำจำกัดความประเภทคอนกรีตโดยไม่มีประเภททั่วไปภายใน
ฉันทดสอบวิธีนี้กับประเภทนี้:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
ซึ่งควรกลับมา IActionResult
typeof(IEnumerable<Foo>)
. จะส่งคืนอาร์กิวเมนต์ทั่วไปแรก - ในกรณีนี้GetGenericArguments()
[0]
typeof(Foo)
นี่คือวิธีที่ฉันมักจะทำ (ผ่านวิธีการขยาย):
public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
{
return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
}
นี่คือเวอร์ชันนิพจน์แบบสอบถาม Linq ที่อ่านไม่ได้ของฉัน ..
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);
}
โปรดทราบว่าเมธอดยังคำนึงถึงวิธีการที่ไม่ใช่แบบทั่วไปIEnumerable
ซึ่งจะส่งคืนobject
ในกรณีนี้เนื่องจากใช้Type
อินสแตนซ์ที่เป็นรูปธรรมมากกว่าอาร์กิวเมนต์ อย่างไรก็ตามสำหรับx แสดงถึงสิ่งที่ไม่รู้จักฉันพบว่าวิดีโอนี้น่าสนใจแม้ว่ามันจะไม่เกี่ยวข้องก็ตาม ..