例:
public static void DoSomething<K,V>(IDictionary<K,V> items) { items.Keys.Each(key => { if (items[key] **is IEnumerable<?>**) { /* do something */ } else { /* do something else */ } }
不用反射就可以做到吗?我该怎么说C#中的IEnumerable?由于IEnumerable <>实现了IEnumerable,我应该只使用IEnumerable吗?
先前接受的答案很好,但这是错误的。值得庆幸的是,该错误很小。IEnumerable如果您真的想了解接口的通用版本,那么仅进行检查是不够的。有很多只实现非通用接口的类。一分钟后,我会给出答案。不过,首先,我想指出,所接受的答案过于复杂,因为在给定的情况下,以下代码将实现相同的效果:
IEnumerable
if (items[key] is IEnumerable)
这样做的好处甚至更多,因为它分别适用于每个项目(而不适用于它们的公共子类V)。
V
现在,寻找正确的解决方案。这有点复杂,因为我们必须采用泛型IEnumerable1(即IEnumerable<>`具有一个类型参数的类型)并注入正确的泛型参数:
(即
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<string>(); var ys = new System.Collections.ArrayList(); Console.WriteLine(IsGenericEnumerable(xs.GetType())); Console.WriteLine(IsGenericEnumerable(ys.GetType()));
产量:
True False
不要太在意使用反射的事实。的确,这确实增加了运行时开销,但使用is运算符也是如此。
is
当然,上面的代码受到严格限制,可以扩展为更通用的方法IsAssignableToGenericType。以下实施方式略有错误1,我仅将其留在 历史上 。 不要使用它 。取而代之的是,James在回答中提供了一个出色而正确的实现。
IsAssignableToGenericType
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); }
1当与genericType相同时失败givenType。由于相同的原因,它对于可为空的类型也会失败,即
genericType
givenType
IsAssignableToGenericType(typeof(List<int>), typeof(List<>)) == false IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false
我已经创建了包含一整套测试用例的要点。