có cách nào để truy xuất loại T
từ IEnumerable<T>
thông qua phản chiếu?
ví dụ
tôi có một IEnumerable<Child>
thông tin khác nhau ; tôi muốn truy xuất kiểu của Child thông qua phản chiếu
có cách nào để truy xuất loại T
từ IEnumerable<T>
thông qua phản chiếu?
ví dụ
tôi có một IEnumerable<Child>
thông tin khác nhau ; tôi muốn truy xuất kiểu của Child thông qua phản chiếu
Câu trả lời:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Do đó,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
bản in System.String
.
Xem MSDN cho Type.GetGenericArguments
.
Chỉnh sửa: Tôi tin rằng điều này sẽ giải quyết các mối quan tâm trong các nhận xét:
// 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]);
}
Một số đối tượng triển khai nhiều hơn một chung IEnumerable
vì vậy cần phải trả về một bảng liệt kê chúng.
Chỉnh sửa: Mặc dù, tôi phải nói rằng, đó là một ý tưởng khủng khiếp đối với một lớp thực hiện IEnumerable<T>
cho nhiều hơn một T
.
Tôi chỉ muốn tạo một phương pháp mở rộng. Điều này đã làm việc với mọi thứ tôi đã ném vào nó.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
Tôi đã có một vấn đề tương tự. Câu trả lời đã chọn phù hợp với các trường hợp thực tế. Trong trường hợp của tôi, tôi chỉ có một loại (từ a PropertyInfo
).
Câu trả lời đã chọn không thành công khi bản thân kiểu typeof(IEnumerable<T>)
không phải là một triển khai của IEnumerable<T>
.
Đối với trường hợp này, các hoạt động sau:
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
- chỉ dành cho phiên bản dotNet FrameWork> = 4.5. Nếu không - hãy sử dụng Type.GetGenericArguments
thay thế.
Nếu bạn biết IEnumerable<T>
(thông qua generic), thì chỉ typeof(T)
cần làm việc. Nếu không (cho object
hoặc không chung chung IEnumerable
), hãy kiểm tra các giao diện được triển khai:
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
tham số thay vì một object obj
tham số: bạn không thể thay thế obj.GetType()
bằng type
bởi vì nếu bạn truyền vào typeof(IEnumerable<T>)
bạn sẽ không nhận được gì. Để giải quyết vấn đề này, hãy kiểm tra type
chính nó để xem nó có phải là IEnumerable<>
giao diện chung của nó hay không.
Cảm ơn bạn rất nhiều về cuộc thảo luận. Tôi đã sử dụng nó làm cơ sở cho giải pháp bên dưới, giải pháp này hoạt động tốt cho tất cả các trường hợp mà tôi quan tâm (IEnumerable, các lớp dẫn xuất, v.v.). Tôi nghĩ tôi nên chia sẻ ở đây trong trường hợp bất kỳ ai cũng cần nó:
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()
Một giải pháp thay thế cho các tình huống đơn giản hơn mà nó sẽ là một IEnumerable<T>
hoặc T
- ghi chú sử dụng GenericTypeArguments
thay vì GetGenericArguments()
.
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Đây là một cải tiến trên giải pháp của Eli Algranti ở chỗ nó cũng sẽ hoạt động khi IEnumerable<>
kiểu ở bất kỳ mức nào trong cây kế thừa.
Giải pháp này sẽ nhận được loại phần tử từ bất kỳ Type
. Nếu kiểu không phải là an IEnumerable<>
, nó sẽ trả về kiểu được truyền vào. Đối với các đối tượng, hãy sử dụng GetType
. Đối với các loại, hãy sử dụng typeof
, sau đó gọi phương thức mở rộng này trên kết quả.
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;
}
}
Tôi biết điều này là hơi cũ, nhưng tôi tin rằng phương pháp này sẽ bao gồm tất cả các vấn đề và thách thức được nêu trong các bình luận. Tín dụng cho Eli Algranti vì đã truyền cảm hứng cho công việc của tôi.
/// <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();
}
Đây là một hàm đệ quy sẽ đi sâu trước danh sách các kiểu chung cho đến khi nó nhận được định nghĩa kiểu cụ thể không có kiểu chung bên trong.
Tôi đã thử nghiệm phương pháp này với loại này:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
cái nào sẽ trở lại IActionResult
typeof(IEnumerable<Foo>)
. sẽ trả về đối số chung đầu tiên - trong trường hợp này .GetGenericArguments()
[0]
typeof(Foo)
Đây là phiên bản biểu thức truy vấn Linq không đọc được của tôi ..
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);
}
Lưu ý rằng phương thức cũng có tính đến tính không chung chung IEnumerable
, nó trả về object
trong trường hợp này, vì nó lấy một Type
đối số chứ không phải một trường hợp cụ thể. Nhân tiện, vì x đại diện cho điều chưa biết , tôi thấy video này hấp dẫn , mặc dù nó không liên quan ..