obtendo tipo T de IEnumerable <T>


106

existe uma maneira de recuperar o tipo Tpor IEnumerable<T>meio da reflexão?

por exemplo

eu tenho uma IEnumerable<Child>informação variável ; eu quero recuperar o tipo de criança através da reflexão


1
Em que contexto? O que é isso IEnumerable <T>? É uma instância de objeto enviada como um argumento? Ou o que?
— Mehrdad Afshari

Respostas:


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

Assim,

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

impressões System.String.

Consulte MSDN para Type.GetGenericArguments.

Edit: Acredito que isso resolverá as preocupações nos comentários:

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

Alguns objetos implementam mais de um genérico, IEnumerableportanto, é necessário retornar uma enumeração deles.

Edit: Embora, devo dizer, seja uma ideia terrível para uma classe implementar IEnumerable<T>para mais de um T.


Ou pior ainda, escreva um método com retornos de rendimento e tente chamar GetType em uma variável criada com esse método. Ele dirá que não é um tipo de evento genérico. Então, basicamente, não há uma maneira universal de obter T dada uma variável de instância do tipo IEnumerable <T>
— Darin Dimitrov

1
Ou tente com a classe MyClass: IEnumerable <int> {}. Esta classe não possui uma interface genérica.
— Stefan Steinegger

1
Por que alguém iria recorrer a obter os argumentos genéricos e, em seguida, obter o tipo de seu indexador? Isso é apenas pedir para o desastre, especialmente quando a linguagem suporta typeof (T) como @amsprich sugere em sua resposta, que também pode ser usado com um tipo genérico ou conhecido ...
— Robert Petz

Isso falha miseravelmente quando usado com consultas linq - o primeiro argumento genérico de um WhereSelectEnumerableIterator não é . Você está obtendo o argumento genérico do objeto subjacente, não a própria interface.
— Pxtl

myEnumerable.GetType (). GetGenericArguments () [0] fornece a propriedade FullName que informa o namespace.classname. Se você estiver procurando apenas o nome da classe, use myEnumerable.GetType (). GetGenericArguments () [0] .Name
— user5534263

38

Eu acabaria de fazer um método de extensão. Isso funcionou com tudo que eu joguei nele.

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

6
Não funcionará se sua referência de tempo de compilação for apenas do tipo objeto.
— Stijn Van Antwerpen

27

Eu tive um problema parecido. A resposta selecionada funciona para instâncias reais. No meu caso, tinha apenas um tipo (de a PropertyInfo).

A resposta selecionada falha quando o próprio tipo typeof(IEnumerable<T>)não é uma implementação de IEnumerable<T>.

Para este caso, funciona o seguinte:

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

Salvou meu dia. Para o meu caso, adicionei uma instrução if separada para manipular strings, uma vez que implementa IEnumerable <char>
— Edmund P Charumbira

Type.GenericTypeArguments- apenas para dotNet FrameWork version> = 4.5. Caso contrário - use em seu Type.GetGenericArgumentslugar.
— Кое Кто

20

Se você conhece o IEnumerable<T>(via genéricos), então typeof(T)deve funcionar. Caso contrário (para object, ou não genérico IEnumerable), verifique as interfaces implementadas:

        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
Alguns objetos implementam mais de um IEnumerable genérico.
— jason

5
@Jason - e nesses casos, a questão de "encontrar o T" já é uma questão duvidosa; Não posso fazer nada a respeito ...
— Marc Gravell

Uma pequena pegadinha para qualquer um que tente usar isso com um Type typeparâmetro em vez de um object objparâmetro: você não pode simplesmente substituir obj.GetType()por typeporque, se você passar, typeof(IEnumerable<T>)não receberá nada. Para contornar isso, teste o typepróprio para ver se é um genérico de IEnumerable<>e, em seguida, suas interfaces.
— Ian Mercer,

8

Muito obrigado pela discussão. Usei-o como base para a solução abaixo, que funciona bem para todos os casos que me interessam (IEnumerable, classes derivadas, etc). Achei que deveria compartilhar aqui, caso alguém precise também:

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

Aqui está um one-liner que faz tudo isso usando o operador condicional nulo: someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
— Mass Dot Net de

2

Apenas use typeof(T)

EDIT: Ou use .GetType (). GetGenericParameter () em um objeto instanciado se você não tiver T.


Você nem sempre tem T.
— jason

Verdadeiro, nesse caso você pode usar .GetType (). Vou modificar minha resposta.
— reinará

2

Uma alternativa para situações mais simples em que será um IEnumerable<T>ou T- observe o uso de em GenericTypeArgumentsvez de GetGenericArguments().

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

    return genericType;
} else {
    return inputType;
}

1

Esta é uma melhoria na solução de Eli Algranti, pois também funcionará onde o IEnumerable<>tipo está em qualquer nível na árvore de herança.

Esta solução obterá o tipo de elemento de qualquer Type. Se o tipo não for um IEnumerable<>, ele retornará o tipo passado. Para objetos, use GetType. Para tipos, use typeofe chame este método de extensão no resultado.

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

Eu sei que isso é um pouco antigo, mas acredito que este método irá cobrir todos os problemas e desafios declarados nos comentários. Os meus agradecimentos a Eli Algranti por inspirar o meu trabalho.

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

Esta é uma função recursiva que irá aprofundar primeiro na lista de tipos genéricos até obter uma definição de tipo concreto sem tipos genéricos internos.

Testei este método com este tipo: ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>

que deve retornar IActionResult



0

é assim que eu costumo fazer (via método de extensão):

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

0

Esta é a minha versão de expressão de consulta Linq ilegível.

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

Observe que o método também leva IEnumerableem consideração o não genérico , ele retorna objectneste caso, porque leva uma Typeinstância em vez de uma instância concreta como o argumento. A propósito, pois x representa o desconhecido , achei este vídeo interessante , embora seja irrelevante.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.