IEnumerable <T>에서 유형 T 가져 오기


106

리플렉션 TIEnumerable<T>통해 유형을 검색하는 방법이 있습니까?

예 :

변수 IEnumerable<Child>정보가 있습니다. 반성을 통해 아이의 타입을 되찾고 싶어


1
어떤 맥락에서? 이 IEnumerable <T>는 무엇입니까? 인수로 보낸 개체 인스턴스입니까? 또는 무엇을?
Mehrdad Afshari

답변:


142
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0]; 

따라서

IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);

인쇄합니다 System.String.

.NETMSDN 을 참조하십시오 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.


또는 더 나쁜 것은 yield return이있는 메서드를 작성하고이 메서드로 만든 변수에 대해 GetType을 호출하려고합니다. 이벤트가 제네릭 유형이 아님을 알려줍니다. 따라서 기본적으로 IEnumerable <T> 유형의 인스턴스 변수가 주어지면 T를 얻는 보편적 인 방법은 없습니다.
Darin Dimitrov

1
또는 MyClass : IEnumerable <int> {} 클래스로 시도하십시오. 이 클래스에는 일반 인터페이스가 없습니다.
Stefan Steinegger

1
왜 누구든지 제네릭 인수를 얻은 다음 인덱서에서 유형을 가져 오는 데 의지할까요? 그것은 단지 재난을 요구하는 것입니다. 특히 @amsprich가 그의 대답에서 제안한 것과 같은 typeof (T)를 지원할 때 일반적인 또는 알려진 유형과 함께 사용할 수 있습니다 ...
Robert Petz

이것은 linq 쿼리와 함께 사용될 때 비참하게 실패합니다 . WhereSelectEnumerableIterator의 첫 번째 일반 인수는 그렇지 않습니다 . 인터페이스 자체가 아닌 기본 개체의 일반 인수를 얻습니다.
Pxtl

myEnumerable.GetType (). GetGenericArguments () [0]은 namespace.classname을 알려주는 FullName 속성을 제공합니다. . 당신은 단지 클래스 이름을 사용 myEnumerable.GetType을 찾고 있다면 () GetGenericArguments () [0] .name을
user5534263

38

나는 확장 방법을 만들 것입니다. 이것은 내가 던진 모든 것과 함께 작동했습니다.

public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
    return typeof(T);
}

6
컴파일 시간 참조가 객체 유형이면 작동하지 않습니다.
Stijn Van Antwerpen

27

비슷한 문제가있었습니다. 선택한 답변은 실제 인스턴스에 적용됩니다. 제 경우에는 (에서 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;
}

내 하루를 구했습니다. 이 IEnumerable을 구현하기 때문에 문이 문자열을 처리하는 경우 저의 경우는 별도의 추가 <문자>
에드먼드 P Charumbira

Type.GenericTypeArguments-dotNet FrameWork 버전> = 4.5에만 해당됩니다. 그렇지 않으면 Type.GetGenericArguments대신 사용하십시오.
Кое Кто

20

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

3
일부 개체는 둘 이상의 일반 IEnumerable을 구현합니다.
jason

5
@Jason-그리고이 경우 "T 찾기"라는 질문은 이미 모호한 질문입니다. 나는 그것에 대해 아무것도 할 수 없습니다 ...
Marc Gravell

로모그래퍼이 사용하려는 사람을위한 하나 개의 작은 잡았다 Type type매개 변수가 아닌 object obj매개 변수 : 당신은 그냥 대체 할 수 obj.GetType()type당신이 통과하면 때문에 typeof(IEnumerable<T>)당신은 아무것도 얻을 수 없다. 이 문제를 해결하려면 type자체를 테스트하여 제네릭인지 확인한 IEnumerable<>다음 해당 인터페이스를 확인하십시오.
Ian Mercer

8

토론 해 주셔서 대단히 감사합니다. 나는 그것을 아래 솔루션의 기초로 사용했는데, 이는 나에게 관심이있는 모든 경우 (IEnumerable, 파생 클래스 등)에서 잘 작동합니다. 누군가가 필요로 할 경우를 대비하여 여기에 공유해야한다고 생각했습니다.

  Type GetItemType(object someCollection)
  {
    var type = someCollection.GetType();
    var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
    return ienum != null
      ? ienum.GetGenericArguments()[0]
      : null;
  }

다음은 null 조건 연산자를 사용하여이 모든 작업을 수행하는 한 줄입니다. someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Mass Dot Net

2

그냥 사용 typeof(T)

편집 : 또는 T가없는 경우 인스턴스화 된 개체에 .GetType (). GetGenericParameter ()를 사용합니다.


항상 T가있는 것은 아닙니다.
jason

True,이 경우 .GetType ()을 사용할 수 있습니다. 내 대답을 수정하겠습니다.
고삐

2

대신에 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;
}

1

이것은 IEnumerable<>유형이 상속 트리의 모든 수준에서 작동한다는 점에서 Eli Algranti의 솔루션을 개선 한 것 입니다.

이 솔루션은 모든 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;
    }
}

1

나는 이것이 조금 오래되었다는 것을 알고 있지만이 방법이 의견에 명시된 모든 문제와 도전을 다룰 것이라고 믿습니다. 내 작업에 영감을 준 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<>);
}

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

이것은 내부 제네릭 유형이없는 구체적인 유형 정의를 얻을 때까지 제네릭 유형 목록 아래로 먼저 깊이 이동하는 재귀 함수입니다.

이 유형으로이 방법을 테스트했습니다. ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>

반환해야하는 IActionResult



0

이것은 내가 일반적으로 수행하는 방법입니다 (확장 방법을 통해).

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

0

다음은 읽을 수없는 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가 알려지지 않은 것을 나타 내기 때문에이 비디오 는 무관하지만 흥미 롭다는 것을 알았 습니다 ..

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.