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

MSDN forを参照してください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、2009年

1
なぜ誰もが一般的な引数を取得し、そのインデクサーから型を取得することに頼るのでしょうか?それは、特に@amsprichが彼/彼女の答えで示唆するようなtypeof(T)をサポートする場合、それは災害を求めているだけです。これは、ジェネリック型または既知の型でも使用できます...
Robert Petz

これは、linqクエリで使用すると惨めに失敗します。WhereSelectEnumerableIteratorの最初の汎用引数はそうではありません。インターフェース自体ではなく、基になるオブジェクトの一般的な引数を取得しています。
Pxtl 2014年

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 <char>を実装しているため、文字列を処理する別のifステートメントを追加しました
Edmund P Charumbira '16

Type.GenericTypeArguments-dotNet FrameWorkバージョン> = 4.5のみ。それ以外の場合- Type.GetGenericArguments代わりに使用してください。
КоеКто

20

IEnumerable<T>(ジェネリックを介して)を知っている場合typeof(T)は、動作するはずです。それ以外の場合(object、またはnon-generic 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を実装しています。
ジェイソン

5
@ジェイソン-そして、そのような場合、「Tを見つける」の質問はすでに疑わしい質問です。私はそれについて何もできません...
マーク・グラベル

これをType typeパラメーターではなくパラメーターで使用しようとする人にとっての1つの小さな落とし穴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;
  }

ここでは、このすべて使用してヌル条件演算子をしてワンライナーです: someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
マスドットネット

2

使うだけ typeof(T)

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


あなたはいつもTを持っているわけではありません
。–ジェイソン

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.