web-dev-qa-db-ja.com

ジェネリッククラスまたはメソッドのメンバからTの型を取得する方法

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

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

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

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

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

        // get type of T
    }
}

私のクラスのリストに含まれているオブジェクトの種類を知りたいのですが。それで、Barまたはローカル変数bazと呼ばれるリストプロパティは、どんなタイプのTを含みますか?

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

611

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

Type typeParameterType = typeof(T);

あなたがobjectを型パラメータとして持っているという幸運な状況にあるならば、 Marcの答え を見てください。

609
Tamas Czinege

(注:私はあなたが知っているのはobjectIListかそれに類似したものであり、そして実行時にリストはどんな型でもあり得ると仮定します)

あなたがそれがList<T>であることを知っているならば、それから:

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

もう1つの選択肢は、インデクサーを調べることです。

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

新しいTypeInfoを使う:

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

次の拡張方法であなたは反射なしで逃げることができます:

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
45
3dGrabber

試します

list.GetType().GetGenericArguments()
30
Rauhotz

それは私のための仕事です。私のリストはどこか未知の種類のリストです。

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

これを考えてみましょう。私はこれを使って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);
    }
}
9
Ferenc Mucsi

Type変数全体を必要とせず、単に型をチェックしたい場合は、一時変数を簡単に作成してis演算子を使用できます。

T checkType = default(T);

if (checkType is MyClass)
{}
9
Sebi

ジェネリックリストの戻り型にこれを使うことができます。

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}
8

GetGenericArgument()メソッドは、インスタンスの基本型(そのクラスは汎用クラスmyClass<T>)に設定する必要があります。それ以外の場合は、タイプ[0]を返します。

例:

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

私はこの拡張メソッドを使って似たようなことを実現しています。

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

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

4
Ken Smith

次のようにして、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> { ... }

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

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

4
Dan Malcolm

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

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

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();
1
fantastory
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}
1
Karanvir Kang

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

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

OR

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

プロパティの基本型を知りたい場合は、これを試してください。

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]
0
Fatih Çelik

たとえばswitch文を使用するために、 文字通り 型の名前を付けようとしている開発者にとっては、これが例です。

    public static void TypeBasedLogic<T>(T item)
    {
        switch (typeof(T).ToString()) //e.g. System.Int32
        {
            case nameof(System) + "." + nameof(Int32):
                //Logic for int
                Console.Write("It's an integer");
            break;

            case nameof(System) + "." + nameof(Double):
                //Logic for double
                Console.Write("It's a double");
            break;

            case nameof(System) + "." + nameof(Decimal):
                //Logic for decimal
                Console.Write("It's a decimal");
            break;

            case nameof(System) + "." + nameof(String):
                //Logic for string
                Console.Write("It's a string");
            break;

            default:
                //logic for the rest other System or custom types
                Console.Write("It's a "+ typeof(T).ToString());
            break;

        }
    }

使用法:

TypeBasedLogic(5); // outputs: It's an integer
0
Shadi Namrouti