C# - 获取泛型列表中的项类型

72

如何获取泛型列表中包含的项的类型?可以很容易地获取集合中的第一项并调用 .GetType(),但我不能保证集合中一定有项。

希望这样说得清楚。

谢谢,
Sonny


关于这个类型,你已经了解些什么?你能提供一个样本上下文吗? - Jon Skeet
9个回答

120
你可以使用 Type.GetGenericArguments 方法来实现此目的。
List<Foo> myList = ...

Type myListElementType = myList.GetType().GetGenericArguments().Single();

5
如果T标记处于作用域内(比如在一个接受List<T>的方法中),你也可以使用typeof(T)。如果List<T>存储在类型为object的变量中,则必须使用上述方法。 - cdhowie
你今天早些时候没有回答过另一个几乎完全相同的问题吗? - LukeH
@LukeH:是的,闪电确实会打两次 :) https://dev59.com/TlLTa4cB1Zd3GeqPcq0l#4448890 - Ani
1
@Lambert:我不是Markdown的专家,但我选择一个块并单击“代码示例”图标。我建议您单击答案框右上角的橙色“?”以获取有关此内容如何工作的更多信息。 - Ani

20

如果需要更加可靠的方法:

public static Type GetListType(object someList)
{
    if (someList == null)
        throw new ArgumentNullException("someList");

    var type = someList.GetType();

    if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(List<>))
        throw new ArgumentException("Type must be List<>, but was " + type.FullName, "someList");

    return type.GetGenericArguments()[0];
}

但如果您的变量类型为 List<T>,那么您可以使用 typeof(T)。例如:

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

注意,实际上您甚至不需要someList参数。这个方法只是一个示例,展示了如果您已经在一个通用方法中,如何使用typeof。如果您可以访问T标记(列表存储在非泛型类型的变量中,例如IListobject等),则只需要使用反射方法。

哇塞,错误处理做得不错。你可能还想添加本地化的错误文本。 ;) - user541686
不错。一个小问题 - 传递给新的ArgumentException的参数顺序是错误的。 - Rhys Jones

13
list.GetType().GetGenericArguments()[0]

5

这里有另一种方法,适用于非泛型集合:

static Type GetItemType(Type collectionType)
{
    return collectionType.GetMethod("get_Item").ReturnType;
}

也就是说,获取指定类型的foo[x]的返回类型。

例如:

// Generic type; prints System.Int32
Console.WriteLine(GetItemType(typeof(List<int>)));

// Non-generic type; prints System.String
Console.WriteLine(GetItemType(typeof(System.Collections.Specialized.StringCollection)));

上面的GetItemType方法存在一些问题:

  • 如果类型没有索引运算符,它会抛出NullReferenceException异常。

  • 如果类型对索引运算符有多个重载(例如this[string]this[int]),它会抛出AmbiguousMatchException异常。

这里是一个更加精细的版本:

public static Type GetItemType(this Type collectionType)
{
    var types =
        (from method in collectionType.GetMethods()
         where method.Name == "get_Item"
         select method.ReturnType
        ).Distinct().ToArray();
    if (types.Length == 0)
        return null;
    if (types.Length != 1)
        throw new Exception(string.Format("{0} has multiple item types", collectionType.FullName));
    return types[0];
}

2
    public Type GetType(IEnumerable<object> resultList)
    {
        return resultList.GetType().GetElementType();
    }

1
虽然这段代码片段可能解决了问题,但包含解释真的有助于提高您的帖子质量。请记住,您正在为未来的读者回答问题,这些人可能不知道您代码建议的原因。请尽量不要在代码中添加过多的解释性注释,因为这会降低代码和解释的可读性! - Blue

2

这个东西很简单,全部都是静态的(例如不需要实例化),而且非常快(没有循环,不使用linq)。这些适用于集合:

    [System.Diagnostics.DebuggerHidden]
    public static Type GetIndexedType(this ICollection poICollection)
    {
        PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetProperty("Item");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this ICollection poICollection)
    {
        PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

还有一些简单的单元测试:

        [Test]
        public void GetIndexedType()
        {
            Assert.AreEqual(null, ((ICollection)null).GetIndexedType());
            Assert.AreEqual(typeof(int), (new List<int>()).GetIndexedType());
            Assert.AreEqual(typeof(bool), (new SortedList<string, bool>()).GetIndexedType());
        }

        [Test]
        public void GetEnumeratedType()
        {
            Assert.AreEqual(null, ((ICollection)null).GetEnumeratedType());
            Assert.AreEqual(typeof(int), (new List<int>()).GetEnumeratedType());
            Assert.AreEqual(typeof(KeyValuePair<string, bool>), (new SortedList<string, bool>()).GetEnumeratedType());
        }

请注意,有两种方式来看待这个问题,一种类型可能由索引器返回,另一种类型可能由枚举器返回。单元测试显示了两种类型。
祝玩得开心, Frans。
附注:对于可枚举对象:
    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this System.Collections.IEnumerable poIEnumerable)
    {
        PropertyInfo oPropertyInfo = poIEnumerable == null ? null : poIEnumerable.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

对于枚举类型:

    [System.Diagnostics.DebuggerHidden]
    public static Type GetEnumeratedType(this System.Collections.IEnumerator poIEnumerator)
    {
        PropertyInfo oPropertyInfo = poIEnumerator == null ? null : poIEnumerator.GetType().GetProperty("Current");
        return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
    }

在这里,您的方法接受每个非泛型版本的接口,但只有在它们实际实现了泛型版本时才能按预期工作。如果您要满足这些要求,最好直接让方法直接接受接口的泛型版本。 - Servy

1

旧问题新方法,使用 dynamic

void Foo(){
   Type type GetTypeT(data as dynamic);
}

private static Type GetTypeT<T>(IEnumerable<T> data)
{
    return typeof(T);
}

1
这是一个与派生类一起使用的解决方案。
因为有了这个类:
  public class SubList : List<int>
  {   }

如果你调用:subList.GetType().GetGenericArguments().Single() 会抛出一个System.InvalidOperationException 使用这种方法可以适用于派生类:
public Type GetListItemType<T>(List<T> list)
{
  Type type = list.GetType();
  while (type != typeof(List<T>))
    type = type.BaseType;
  return type.GetGenericArguments().Single();
}


var list = new List<int>();
var subList = new SubList();
Console.WriteLine(GetListItemType(list)); // System.Int32
Console.WriteLine(GetListItemType(subList)); // System.Int32

0
Public Shared Function ListItemType(ListType As System.Type) As System.Type

  If Not ListType.IsGenericType Then
    If ListType.BaseType IsNot Nothing AndAlso ListType.BaseType.IsGenericType Then
      Return ListItemType(ListType.BaseType)
    End If
  Else
    Return ListType.GetGenericArguments.Single
  End If
End Function

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