ジェネリッククラスまたはメソッドのメンバーからTの型を取得するにはどうすればよいですか?


674

クラスまたはメソッドにジェネリックメンバーがあるとしましょう。

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

クラスをインスタンス化すると、はにTなるMyTypeObject1ので、クラスには一般的なリストプロパティがありますList<MyTypeObject1>。同じことが非ジェネリッククラスのジェネリックメソッドにも当てはまります。

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

私のクラスのリストに含まれるオブジェクトのタイプを知りたいのですが。したがって、呼び出されたリストプロパティBarまたはローカル変数bazには、どのようなタイプが含まれていTますか?

Bar[0].GetType()リストに要素が含まれていない可能性があるため、私はできません。どうすればできますか?

回答:


693

私が正しく理解していれば、リストにはコンテナークラス自体と同じ型パラメーターがあります。この場合、次のようになります。

Type typeParameterType = typeof(T);

object型パラメーターとして持つという幸運な状況にある場合は、Marcの回答を参照してください。


4
笑-はい、そうです。OPには、、または同様の機能しかないと想定していましたがobjectIListこれは非常に適切な答えになる可能性があります。
Marc Gravell

32
読みやすさtypeofが大好きです。あなたは、Tの種類を知りたい場合は、単に使用typeof(T):)
demoncodemonkey

2
私は実際にはtypeof(Type)を使用しただけで、うまく機能します。
アントン

1
ただし、ジェネリックパラメーターでtypeof()を使用することはできません。
Reynevan

2
@Reynevanもちろんtypeof()、ジェネリックパラメーターで使用できます。それが機能しない例はありますか?または、型パラメーターと参照を混同していますか?
Luaan

519

(注:私が知っているのはobjectIListまたは類似のものであり、リストは実行時に任意のタイプであるとます)

それがであることがわかっている場合List<T>

Type type = abc.GetType().GetGenericArguments()[0];

別のオプションは、インデクサーを確認することです。

Type type = abc.GetType().GetProperty("Item").PropertyType;

新しいTypeInfoの使用:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
タイプtype = abc.GetType()。GetGenericArguments()[0]; ==>範囲外の配列のインデックス...
Patrick Desjardins、

28
@Daok:それはリストではありません<T>
Marc Gravell

BindingList、List、または<T>を保持するオブジェクトに何かが必要です。私がやっていることは、カスタムのBindingListView <T>を使用しています
Patrick Desjardins

1
BindingList <T>を試してみます。BindingListView<T>はBindingList <T>から継承します。両方とも、両方のオプションを試してみましたが、機能しません。私は何か間違ったことをするかもしれません...しかし、この解決策はList <T>型では機能しますが、他の型のリストでは機能しないと思います。
Patrick Desjardins、

タイプtype = abc.GetType()。GetProperty( "Item")。PropertyType; MyObjectの代わりにBindingListView <MyObject>を返します...
Patrick Desjardins

49

次の拡張メソッドを使用すると、リフレクションなしで回避できます。

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

またはより一般的な:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

使用法:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

10
これはT、コンパイル時にのタイプがわかっている場合にのみ役立ちます。その場合、コードはまったく必要ありません。
2015

'return default(T);'
ファンタストーリー

1
@recursive:匿名型のリストを扱う場合に便利です。
JJJ 2016年

私はあなたの答えを読む前にまったく同じことをしましたが、私は私に電話しましたItemType
toddmo

31

試す

list.GetType().GetGenericArguments()

7
新しいList <int>()。GetType()。GetGenericArguments()は、ここにSystem.Int32をエントリとして持つSystem.Type [1]を返します
Rauhotz

@Rauhotz GetGenericArgumentsメソッドはのArrayオブジェクトを返します。このオブジェクトのうちType、必要なジェネリック型の位置を解析する必要があります。以下のようなType<TKey, TValue>:あなたがする必要があるでしょうGetGenericArguments()[0]得るためにTKey種類をしてGetGenericArguments()[1]もらうためにTValueタイプ
GoldBishop

14

それは私の仕事です。myListは、知られていない種類のリストです。

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
型引数が必要であるというエラーが表示されます(つまり<T>
Joseph Humfrey

ジョセフと他、エラーを取り除くためにそれはSystem.Collectionsにあります。
user2334883

1
2行目だけが必要です。A Listはすでにの実装でIEnumerableあるため、キャストは何も追加しないようです。しかし、おかげで、それは良い解決策です。
pipedreambomb 2016年

10

Type変数全体を必要とせず、タイプをチェックするだけの場合は、一時変数を簡単に作成して、is演算子を使用できます。

T checkType = default(T);

if (checkType is MyClass)
{}


8

これを検討してください:私はそれを使用して、20の型付きリストを同じ方法でエクスポートします。

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

1
Tが実際に追加されたオブジェクトの抽象スーパークラスである場合、これは機能しません。言うまでもなく、と同じnew T();ことを行うだけ(T)Activator.CreateInstance(typeof(T));です。それはあなたが追加する必要がないwhere T : new()クラス/関数の定義に、しかし、あなたがあればしたいオブジェクトを作るために、それはとにかく行われるべきです。
Nyerguds 2013

また、エントリを呼び出すGetTypeと、FirstOrDefaultnull参照例外が発生する可能性があります。少なくとも1つのアイテムが返されることが確実な場合は、First代わりに使用しないでください。
Mathias Lykkegaard Lorenzen 2015

6

私はこの拡張メソッドを使用して同様のことを達成します:

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

次のように使用します。

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

これはあなたが探しているものではありませんが、関連するテクニックを示すのに役立ちます。


こんにちは。回答ありがとうございます。 "StripStartingWith"の拡張機能も追加できますか?
セドリックアーノールド

1
@CedricArnould-追加されました。
ケン・スミス

5

GetGenericArgument()この方法は、(そのクラスジェネリッククラスでインスタンスのベースタイプに設定する必要がありますmyClass<T>)。それ以外の場合は、タイプ[0]を返します

例:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

次のようにして、IEnumerable <T>を実装する任意のコレクション型から「T」の型を取得できます。

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

IEnumerable <T>を2回実装するコレクション型はサポートされていないことに注意してください。

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

これをサポートする必要がある場合は、型の配列を返すことができると思います...

また、これを頻繁に実行している場合(ループなど)、コレクションタイプごとに結果をキャッシュすることもできます。


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
これは、型がリストのようなものかどうかという問題に対処しているようですが、問題は、リストであることがわかっている型が既に初期化されているジェネリック型パラメーターを特定する方法についてです。
Nathan Tuggy

1

3dGrabberのソリューションを使用する:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

プロパティの基になるタイプを知りたい場合は、次のことを試してください。

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

これは私がやった方法です

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

次に、このように呼び出します

var item = Activator.CreateInstance(iListType.GetElementType());

または

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

タイプ:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
テストするリストの要素がリストにない場合、これはNullReferenceExceptionをスローします。
rossisdead 2010

1
SingleOrDefault()InvalidOperationException2つ以上の要素がある場合にもスローされます。
devgeezer 2012年

\ @rossisdeadと\ @devgeezerによって正しく指摘されているように、この答えは間違っています。
Oliver
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.