C#泛型列表<T>如何获取T的类型?

157
我正在进行一个反射项目,现在卡住了。
如果我有一个可以容纳List<SomeClass>myclass对象,如果属性myclass.SomList为空,有谁知道如何获取下面代码中的类型?
List<myclass> myList = dataGenerator.getMyClasses();
lbxObjects.ItemsSource = myList; 
lbxObjects.SelectionChanged += lbxObjects_SelectionChanged;

private void lbxObjects_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    Reflect();
}

Private void Reflect()
{
    foreach (PropertyInfo pi in lbxObjects.SelectedItem.GetType().GetProperties())
    {
        switch (pi.PropertyType.Name.ToLower())
        {
            case "list`1":
            {           
                // This works if the List<T> contains one or more elements.
                Type tTemp = GetGenericType(pi.GetValue(lbxObjects.SelectedItem, null));

                // but how is it possible to get the Type if the value is null? 
                // I need to be able to create a new object of the type the generic list expect. 
                // Type type = pi.getType?? // how to get the Type of the class inside List<T>?
                break;
            }
        }
    }
}

private Type GetGenericType(object obj)
{
    if (obj != null)
    {
        Type t = obj.GetType();
        if (t.IsGenericType)
        {
            Type[] at = t.GetGenericArguments();
            t = at.First<Type>();
        } 
        return t;
    }
    else
    {
        return null;
    }
}
4个回答

285
Type type = pi.PropertyType;
if(type.IsGenericType && type.GetGenericTypeDefinition()
        == typeof(List<>))
{
    Type itemType = type.GetGenericArguments()[0]; // use this...
}
更一般地说,要支持任何 IList<T>,您需要检查接口:
foreach (Type interfaceType in type.GetInterfaces())
{
    if (interfaceType.IsGenericType &&
        interfaceType.GetGenericTypeDefinition()
        == typeof(IList<>))
    {
        Type itemType = type.GetGenericArguments()[0];
        // do something...
        break;
    }
}

9
不应该是Type itemType = interfaceType.GetGenericArguments()[0];吗? - Muxa
有趣的是,第二个例子在使用IList<int>时会失败。请参照以下链接进行修复:https://dev59.com/YXNA5IYBdhLWcg3wPLL-#13608408 - Colonel Panic
我知道这个答案很老,但我试图理解以下内容:'GetGenericArguments()[0];' 为什么要加上0?Type[] 中是否总是只有一个项目? - Ismoh
1
因为调用GetGenericArguments()返回一个数组。https://msdn.microsoft.com/zh-cn/library/system.type.getgenericarguments(v=vs.110).aspx 一个List只有一种类型。例如,一个字典会有两种类型。 - DoomVroom

24

如果我有一个对象,我怀疑它是某种类型的IList<>,我该如何确定它是哪种类型的IList<>

以下是大胆的解决方案。它假设您有要测试的实际对象(而不是Type)。

public static Type ListOfWhat(Object list)
{
    return ListOfWhat2((dynamic)list);
}

private static Type ListOfWhat2<T>(IList<T> list)
{
    return typeof(T);
}

示例用法:

object value = new ObservableCollection<DateTime>();
ListOfWhat(value).Dump();

打印

typeof(DateTime)

为什么要使用IList<T>而不是IEnumerable<T>?为什么要使用dynamic - John Saunders
我是否误解了OP的问题?细节不清楚,所以我回答了标题中的问题。 - Colonel Panic
5
您是个巫师!我一直在苦苦思索如何解决一个动态解析器与通用数据结构(可以处理单个类型和列表)的问题,而这个让我找到了正确的方向。 - Steve Py
“动态”使用的绝佳示例 - irriss

12

马克的回答是我用于此目的的方法,但为了简单起见(和更友好的API?),如果您有一个集合基类,则可以定义一个属性,例如:

public abstract class CollectionBase<T> : IList<T>
{
   ...

   public Type ElementType
   {
      get
      {
         return typeof(T);
      }
   }
}

我发现这种方法很有用,并且对于任何新手来说都很容易理解泛型。

我想使用这种方法,但后来意识到为了确定元素类型,我必须拥有列表实例,而我并不总是拥有它。 - Muxa
1
你可以将该属性设为静态的,例如 "public static Type ElementType" ... 然后可以通过 "var t = CollectionBase<int>.ElementType;" 访问它 ... 这样就不需要实例变量。 - Nabheet
True。Static允许您在不存在对象的情况下使用类。我认为这种方法更容易使用。当然,您需要在对象中存储额外的信息,但至少您可以避免在批准的答案中进行级联调用。 - rbaleksandar

10

如果我怀疑一个对象是某种类型的IList<>,我该如何确定它是什么类型的IList<>

下面是可靠的解决方案。很抱歉会有点长 - C#的反射API让这个问题变得非常困难。

/// <summary>
/// Test if a type implements IList of T, and if so, determine T.
/// </summary>
public static bool TryListOfWhat(Type type, out Type innerType)
{
    Contract.Requires(type != null);

    var interfaceTest = new Func<Type, Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>) ? i.GetGenericArguments().Single() : null);

    innerType = interfaceTest(type);
    if (innerType != null)
    {
        return true;
    }

    foreach (var i in type.GetInterfaces())
    {
        innerType = interfaceTest(i);
        if (innerType != null)
        {
            return true;
        }
    }

    return false;
}

使用示例:

    object value = new ObservableCollection<int>();
Type innerType;
TryListOfWhat(value.GetType(), out innerType).Dump();
innerType.Dump();

返回
True
typeof(Int32)

我没有看到其他结果,与马克的方法相同(使用马克也得到Int32)。 - Offler

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