如何检测类型是否是另一种通用类型

例:

public static void DoSomething(IDictionary items) { items.Keys.Each(key => { if (items[key] **is IEnumerable**) { /* do something */ } else { /* do something else */ } } 

这可以不使用reflection吗? 我怎么说C#中的IEnumerable? 我应该只使用IEnumerable,因为IEnumerable 实现IEnumerable?

以前接受的答案很好但是错了。 值得庆幸的是,错误很小。 如果你真的想知道接口的通用版本,检查IEnumerable是不够的; 有很多类只实现非generics接口。 我会在一分钟内给出答案。 首先,我想指出接受的答案过于复杂,因为以下代码在给定的情况下会达到相同的效果:

 if (items[key] is IEnumerable) 

这甚至更多,因为它分别适用于每个项目(而不是它们的公共子类, V )。

现在,为了正确的解决方案。 这有点复杂,因为我们必须采用generics类型IEnumerable`1 (即带有一个类型参数的类型IEnumerable<> )并注入正确的generics参数:

 static bool IsGenericEnumerable(Type t) { var genArgs = t.GetGenericArguments(); if (genArgs.Length == 1 && typeof(IEnumerable<>).MakeGenericType(genArgs).IsAssignableFrom(t)) return true; else return t.BaseType != null && IsGenericEnumerable(t.BaseType); } 

您可以轻松地测试此代码的正确性:

 var xs = new List(); var ys = new System.Collections.ArrayList(); Console.WriteLine(IsGenericEnumerable(xs.GetType())); Console.WriteLine(IsGenericEnumerable(ys.GetType())); 

收益率:

 True False 

不要过分担心这会使用reflection。 虽然这确实增加了运行时开销,但运算符的使用也是如此。

当然,上面的代码非常受限制,可以扩展为更普遍适用的方法IsAssignableToGenericType 。 以下实现略有不正确1 ,我将其留在这里仅用于历史目的不要使用它 。 相反, 詹姆斯在他的回答中提供了一个优秀,正确的实现。

 public static bool IsAssignableToGenericType(Type givenType, Type genericType) { var interfaceTypes = givenType.GetInterfaces(); foreach (var it in interfaceTypes) if (it.IsGenericType) if (it.GetGenericTypeDefinition() == genericType) return true; Type baseType = givenType.BaseType; if (baseType == null) return false; return baseType.IsGenericType && baseType.GetGenericTypeDefinition() == genericType || IsAssignableToGenericType(baseType, genericType); } 

1genericTypegenericType相同时givenType ; 出于同样的原因,它可以为可空类型,即

 IsAssignableToGenericType(typeof(List), typeof(List<>)) == false IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false 

我用一套全面的测试用例创建了一个要点 。

非常感谢这篇文章。 我想提供一个对我来说效果更好的Konrad Rudolph解决方案。 我在该版本上遇到了一些小问题,特别是在测试Type是否为可为空的值类型时:

 public static bool IsAssignableToGenericType(Type givenType, Type genericType) { var interfaceTypes = givenType.GetInterfaces(); foreach (var it in interfaceTypes) { if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType) return true; } if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) return true; Type baseType = givenType.BaseType; if (baseType == null) return false; return IsAssignableToGenericType(baseType, genericType); } 

关于generics类型和使用IsAssignableFrom()的警告……

说你有以下内容:

 public class MyListBase : IEnumerable where T : ItemBase { } public class MyItem : ItemBase { } public class MyDerivedList : MyListBase { } 

在基本列表类型或派生列表类型上调用IsAssignableFrom将返回false,但显然MyDerivedListinheritanceMyListBase 。 (Jeff的快速说明,generics绝对必须包含在代码块中或倾斜以获取 ,否则它被省略。这是否意图?)问题源于MyListBase被视为完全的事实与MyListBase不同的类型。 以下文章可以更好地解释这一点。 http://mikehadlow.blogspot.com/2006/08/reflecting-generics.html

相反,请尝试以下递归函数:

  public static bool IsDerivedFromGenericType(Type givenType, Type genericType) { Type baseType = givenType.BaseType; if (baseType == null) return false; if (baseType.IsGenericType) { if (baseType.GetGenericTypeDefinition() == genericType) return true; } return IsDerivedFromGenericType(baseType, genericType); } 

/编辑:Konrad的新post考虑了通用递归以及接口。 非常好的工作。 🙂

/ EDIT2:如果检查genericType是否是接口,则可以实现性能优势。 检查可以是当前接口代码周围的if块,但如果您对使用.NET 3.5感兴趣,我的一位朋友提供以下内容:

  public static bool IsAssignableToGenericType(Type givenType, Type genericType) { var interfaces = givenType.GetInterfaces().Where(it => it.IsGenericType).Select(it => it.GetGenericTypeDefinition()); var foundInterface = interfaces.FirstOrDefault(it => it == genericType); if (foundInterface != null) return true; Type baseType = givenType.BaseType; if (baseType == null) return false; return baseType.IsGenericType ? baseType.GetGenericTypeDefinition() == genericType : IsAssignableToGenericType(baseType, genericType); } 
 if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) { 

感谢您提供的精彩信息。 为方便起见,我将其重构为扩展方法并将其简化为单个语句。

 public static bool IsAssignableToGenericType(this Type givenType, Type genericType) { return givenType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType) || givenType.BaseType != null && (givenType.BaseType.IsGenericType && givenType.BaseType.GetGenericTypeDefinition() == genericType || givenType.BaseType.IsAssignableToGenericType(genericType)); } 

现在可以通过以下方式轻松调用:

sometype.IsAssignableToGenericType(typeof运算(MyGenericType <>))

我使用重载:

 public static void DoSomething(IDictionary items) where V : IEnumerable { items.Keys.Each(key => { /* do something */ }); } public static void DoSomething(IDictionary items) { items.Keys.Each(key => { /* do something else */ }); } 

我不确定我明白你的意思。 您想知道对象是否属于任何generics类型,或者您是否要测试它是否是特定的generics类型 ? 或者你只是想知道是否可以枚举?

我不认为第一个是可能的。 第二种肯定是可能的,只需将其视为任何其他类型。 对于第三个,只需按照建议对IEnumerable进行测试。

此外,您不能在类型上使用’is’运算符。

 // Not allowed if (string is Object) Foo(); // You have to use if (typeof(object).IsAssignableFrom(typeof(string)) Foo(); 

有关详细信息,请参阅有关类型的此问题 也许它会帮助你。

您想要查看Type.IsInstanceOfType方法

我曾经认为这种情况可以通过类似于@Thomas Danecker的解决方案来解决 ,但添加了另一个模板参数:

 public static void DoSomething(IDictionary items) where V : IEnumerable { /* do something */ } public static void DoSomething(IDictionary items) { /* do something else */ } 

但我现在注意到它不起作用,除非我明确指定第一个方法的模板参数。 这显然不是字典中每个项目的自定义,但它可能是一种穷人的解决方案。

如果有人能指出我在这里可能做的任何不正确的事情,我将非常感激。