关于c#:如何从泛型类或方法的成员中获取T的类型?

How to get the type of T from a member of a generic class or method?

假设我在类或方法中有一个泛型成员,那么:

1
2
3
4
5
6
7
8
9
public class Foo<T>
{
    public List<T> Bar { get; set; }

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

当我实例化类时,T变为MyTypeObject1,因此类有一个泛型列表属性:List。这同样适用于非泛型类中的泛型方法:

1
2
3
4
5
6
7
8
9
public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

我想知道我的类列表中包含的对象类型。那么名为Bar或局部变量baz的list属性包含什么类型的T

我不能执行Bar[0].GetType(),因为列表可能包含零元素。我该怎么做?


如果我理解正确,您的列表与容器类本身具有相同的类型参数。如果是这样,那么:

1
Type typeParameterType = typeof(T);

如果您有幸使用object作为类型参数,请参阅Marc的答案。


(注:我假设您所知道的是objectIList或类似的,并且列表在运行时可以是任何类型)

如果你知道它是一个List,那么:

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

另一种选择是查看索引器:

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

使用新的类型信息:

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


通过以下扩展方法,您可以在不进行反射的情况下离开:

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

或更一般:

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

用途:

1
2
3
4
5
6
7
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


尝试

1
list.GetType().GetGenericArguments()


这是我的工作。其中mylist是某种未知的列表。

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


考虑一下:我用它以同样的方式导出20个类型列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}


如果您不需要整个类型变量,只想检查类型,您可以轻松创建一个临时变量,并使用is operator。

1
2
3
4
T checkType = default(T);

if (checkType is MyClass)
{}

您可以将此类型用于泛型列表的返回类型:

1
2
3
4
5
public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

必须根据实例的基类型设置GetGenericArgument()方法(其类是通用类myClass)。否则,它返回一个类型[0]

例子:

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

我使用这个扩展方法来完成类似的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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;
}

你这样使用它:

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

这不完全是你想要的,但它有助于展示所涉及的技术。


您可以从实现IEnumerable的任何集合类型中获取"t"类型,方法如下:

1
2
3
4
5
6
7
8
9
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两次的集合类型,例如

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

我想如果需要支持这一点,您可以返回一个类型数组…

此外,如果您经常这样做(例如在循环中),您可能还希望缓存每个集合类型的结果。


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


使用3Drabber的解决方案:

1
2
3
4
5
6
7
8
9
public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now

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

对于那些试图直接处理类型名(例如使用switch语句)的开发人员,下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    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;

        }
    }

用途:

1
TypeBasedLogic(5); // outputs: It's an integer

如果要了解属性的基础类型,请尝试以下操作:

1
propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

我就是这样做的

1
2
3
4
5
6
7
8
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);
}

那就这样说吧

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

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


Type:

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