如何从泛型类或方法的成员中获取T的类型

841
假设我有一个类或方法中的通用成员,如下所示:
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(),因为该列表可能不包含任何元素。我该如何做?

17个回答

878

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

Type typeParameterType = typeof(T);

如果您有幸将 object 作为类型参数,可以参考Marc的回答


69
我喜欢typeof的易读性。如果你想知道变量T的类型,只需使用typeof(T) :) - demoncodemonkey
2
我实际上只是使用了 typeof(Type) ,它非常好用。 - Anton
3
你不能在泛型参数中使用 typeof(),不过。 - Reynevan
8
当然可以在泛型参数中使用typeof()。你有任何不能工作的例子吗?或者是你混淆了类型参数和引用? - Luaan

582

注意:我假设您只知道 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];

3
类型 type = abc.GetType().GetGenericArguments()[0]; ==> 数组索引越界错误... - Patrick Desjardins
34
那么它就不是一个List<T>。 - Marc Gravell
1
尝试使用BindingList<T>,我们的BindingListView<T>继承自BindingList<T>,我已经尝试了这两个选项,但都不起作用。我可能做错了什么......但是我认为这个解决方案适用于List<T>类型,而不是其他类型的列表。 - Patrick Desjardins
2
类型 type = abc.GetType().GetProperty("Item").PropertyType; 返回 BindingListView<MyObject>,而不是 MyObject... - Patrick Desjardins
只是一个小的补充:如果存在多个索引器,GetProperty("Item")将在(罕见的?)情况下抛出System.Reflection.AmbiguousMatchException。我通过try..catch处理这种情况:尝试使用GetProperty,否则退回到GetGenericArguments。 - Michael Stum
显示剩余7条评论

61

使用以下扩展方法,您可以避免使用反射:

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

16
只有在编译时您已经知道 T 的类型时,这才有用。此时,您实际上根本不需要任何代码。 - recursive
2
@recursive:如果你正在使用匿名类型的列表,这将非常有用。 - JJJ

34

尝试一下

list.GetType().GetGenericArguments()

7
new List<int>().GetType().GetGenericArguments() 返回 System.Type[1],其中包含 System.Int32 作为项。 - Rauhotz
1
@Rauhotz,GetGenericArguments 方法返回一个 Type 的数组对象,你需要解析出所需的泛型类型的位置。例如 Type<TKey, TValue>:你需要使用 GetGenericArguments()[0] 获取 TKey 类型,使用 GetGenericArguments()[1] 获取 TValue 类型。 - GoldBishop

27

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

T checkType = default(T);

if (checkType is MyClass)
{}

像专业人士一样编写代码 :) - Ebrahim Karimi
@EbrahimKarimi 当然没问题 :-) - Sebi
4
完全错误,相反的,我们可以使用if (typeof(T) == typeof(Person)) - Võ Quang Hòa
1
@VõQuangHòa 我完全赞同你的评论。你的评论是这里的正确答案。 - Daniel Ibanga
@VõQuangHòa 是正确的。 - Jussi Palo
显示剩余2条评论

17

以下内容对我有效。其中myList是某种未知类型的列表。

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

1
我收到一个错误,它需要一个类型参数(即 <T>)。 - Joseph Humfrey
Joseph和其他人,为了消除错误,需要在System.Collections中进行操作。 - user2334883
1
对我来说,只需要第二行。List已经是IEnumerable的实现,所以强制转换似乎没有什么作用。但还是谢谢,这是一个好的解决方案。 - pipedreambomb
这对我有用,因为我有一个引用类型,它可以并且经常没有任何项 - 其他答案不起作用。 - Arkaine80

15
你可以将这个作为泛型列表的返回类型使用:
public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

9
GetGenericArgument()方法必须设置在你的实例的基类上(如果它的类是一个泛型类myClass<T>)。否则,它将返回一个类型[0]。

示例:

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

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
此外,您正在对“FirstOrDefault”条目调用“GetType”,这可能会导致空引用异常。如果您确定它将返回至少一个项目,为什么不改用“First”呢? - Mathias Lykkegaard Lorenzen

8
我使用这个扩展方法来完成类似的任务:
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>>");
}

虽然这不完全符合您的要求,但它有助于演示所涉及的技术。


需要解释一下。例如,它的要点是什么?这个想法是什么?请通过编辑您的答案来回复,而不是在评论中回复(不包括“编辑:”,“更新:”或类似内容 - 答案应该看起来像今天写的)。 - Peter Mortensen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接