nhận loại T từ IEnumerable <T>


106

có cách nào để truy xuất loại Ttừ 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


1
Trong bối cảnh nào? IEnumerable <T> này là gì? Nó có phải là một cá thể đối tượng được gửi dưới dạng đối số không? Hay cái gì?
Mehrdad Afshari

Câu trả lời:


142
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 IEnumerablevì 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.


Hoặc thậm chí tệ hơn là viết một phương thức với kết quả trả về và cố gắng gọi GetType trên một biến được tạo bằng phương thức này. Nó sẽ cho bạn biết rằng nó không phải là một loại sự kiện chung chung. Vì vậy, về cơ bản không có cách nào phổ biến để có được T đưa ra một biến thể hiện của kiểu IEnumerable <T>
Darin Dimitrov

1
Hoặc thử với lớp MyClass: IEnumerable <int> {}. Lớp này không có giao diện chung.
Stefan Steinegger

1
Tại sao mọi người lại dùng đến việc lấy các đối số chung chung và sau đó lấy kiểu từ trình chỉ mục của nó? Đó chỉ là hỏi cho thiên tai, đặc biệt là khi sự hỗ trợ ngôn ngữ typeof (T) như @amsprich gợi ý trong câu trả lời của anh ấy / cô ấy, mà cũng có thể được sử dụng với một trong hai một chung hoặc một loại nổi tiếng ...
Robert Petz

Điều này không thành công khi được sử dụng với các truy vấn linq - đối số chung đầu tiên của WhereSelectEnumerableIterator thì không . Bạn đang nhận được đối số chung của đối tượng cơ bản, không phải của chính giao diện.
Pxtl,

myEnumerable.GetType (). GetGenericArguments () [0] cung cấp cho bạn thuộc tính FullName cho bạn biết namespace.classname. . Nếu bạn đang tìm kiếm chỉ dành cho các myEnumerable.GetType tên lớp sử dụng () GetGenericArguments () [0] .name
user5534263

38

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);
}

6
Nó sẽ không hoạt động nếu tham chiếu thời gian biên dịch của bạn chỉ là đối tượng kiểu.
Stijn Van Antwerpen

27

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;
}

Đã lưu ngày của tôi. Đối với trường hợp của tôi, tôi đã thêm một riêng biệt câu lệnh if để xử lý chuỗi vì nó thực hiện IEnumerable <char>
Edmund P Charumbira

Type.GenericTypeArguments- chỉ dành cho phiên bản dotNet FrameWork> = 4.5. Nếu không - hãy sử dụng Type.GetGenericArgumentsthay thế.
Кое Кто

20

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 objecthoặ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);

3
Một số đối tượng triển khai nhiều hơn một IEnumerable chung.
jason

5
@Jason - và trong những trường hợp đó, câu hỏi "tìm chữ T" đã là một câu hỏi khó hiểu; Tôi không thể làm bất cứ điều gì về điều đó ...
Marc Gravell

Một gotcha nhỏ dành cho bất kỳ ai đang cố gắng sử dụng điều này với một Type typetham số thay vì một object objtham số: bạn không thể thay thế obj.GetType()bằng typebở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 typechính nó để xem nó có phải là IEnumerable<>giao diện chung của nó hay không.
Ian Mercer

8

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;
  }

Dưới đây là một lớp lót mà làm tất cả bằng cách sử dụng vô điều hành có điều kiện này: someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Khối lượng Dot Net

2

Chỉ dùng typeof(T)

CHỈNH SỬA: Hoặc sử dụng .GetType (). GetGenericParameter () trên một đối tượng khởi tạo nếu bạn không có T.


Không phải lúc nào bạn cũng có T.
jason

Đúng, trong trường hợp đó bạn có thể sử dụng .GetType (). Tôi sẽ sửa đổi câu trả lời của mình.
kiềm chế

2

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 GenericTypeArgumentsthay vì GetGenericArguments().

Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
    && ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {

    return genericType;
} else {
    return inputType;
}

1

Đâ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;
    }
}

1

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<>);
}

1
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



0

đây là cách tôi thường làm (thông qua phương thức mở rộng):

public static Type GetIEnumerableUnderlyingType<T>(this T iEnumerable)
    {
        return typeof(T).GetTypeInfo().GetGenericArguments()[(typeof(T)).GetTypeInfo().GetGenericArguments().Length - 1];
    }

0

Đâ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ề objecttrong 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 ..

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.