web-dev-qa-db-ja.com

IEnumerable <T>から型Tを取得する

リフレクションを介してIEnumerable<T>からT型を取得する方法はありますか?

例えば.

変数IEnumerable<Child> infoがあります。リフレクションを介して子供のタイプを取得したい

96
Usman Masood
_IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0]; 
_

したがって、

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

_System.String_を出力します。

_Type.GetGenericArguments_については、 [〜#〜] msdn [〜#〜] を参照してください。

編集:これはコメントの懸念に対処すると信じています:

_// 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を実装しているため、それらの列挙を返す必要があります。

Edit:言わなければならないが、クラスが複数のTに対して_IEnumerable<T>_を実装するのはひどい考えだ。

132
jason

拡張メソッドを作成するだけです。これは私が投げたすべてのものでうまくいきました。

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

同様の問題がありました。選択した回答は、実際のインスタンスに対して機能します。私の場合、タイプは(PropertyInfoから)のみでした。

タイプ自体が_IEnumerable<T>_の実装ではなくtypeof(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;
}
_
23
Eli Algranti

_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);
_
19
Marc Gravell

議論していただきありがとうございます。私は、以下のソリューションの基礎としてそれを使用しました。これは、私にとって関心のあるすべてのケース(IEnumerable、派生クラスなど)でうまく機能します。誰かがそれを必要とする場合に備えて、ここで共有すべきだと思った:

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

単にtypeof(T)を使用してください

EDIT:または、Tがない場合は、インスタンス化されたオブジェクトで.GetType()。GetGenericParameter()を使用します。

2
rein

_IEnumerable<T>_またはTになる単純な状況の代替-GetGenericArguments()の代わりにGenericTypeArgumentsを使用することに注意してください。

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

    return genericType;
} else {
    return inputType;
}
_
2
Rob Church

これは、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
Neo

私はこれが少し古いことを知っていますが、この方法はコメントで述べられたすべての問題と課題をカバーすると信じています。私の仕事に刺激を与えてくれた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
dahall

typeof(IEnumerable<Foo>). GetGenericArguments() _[0]_は最初の汎用引数を返します-この場合はtypeof(Foo)

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は不明を表しているため、 このビデオ 意味がありませんが、関係ありません..

0
Ken Kin
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
Tyler Huskins

これは私が通常行う方法です(拡張方法を介して):

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