2008-09-16 128 views
22

例如:如何检测如果类型是另一个泛型类型

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?

回答

35

The previously accepted answer是好的,但它是错误的。谢天谢地,这个错误是一个小问题。如果您真的想知道接口的通用版本,检查IEnumerable是不够的;有很多类仅实现非通用接口。我会在一分钟内给出答案。不过,首先我想指出的是,公认的答案是过于复杂,因为下面的代码将实现在特定情况下是相同的:

if (items[key] is IEnumerable) 

这确实更因为它分别适用于每个项目(而不是它们的共同子类,V)。

现在,为正确的解决方案。这是一个比较复杂一点,因为我们必须采取泛型类型IEnumerable`1(即用一个类型参数的类型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运算符也是如此。

当然,上面的代码是非常有限的,可以扩展到一个更普遍适用的方法,IsAssignableToGenericType。下面的实现是有点不正确的,我会在这里留下它仅用于历史目的请勿使用。相反,James has provided an excellent, correct implementation in his answer.

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); 
} 

genericType相同givenType它将失败;出于同样的原因,它不能为空类型,即

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

我创建了一个gist with a comprehensive suite of test cases

+0

这正是我所期待的。谢谢! 它仍然使用反射,但我正在寻找“typeof(IEnumerable <>)”语法。谢谢! – 2008-09-16 18:47:03

+0

除了使用“is”之外,没有其他方法可以对类型进行检查吗? – 2008-09-16 19:22:21

+0

最后一行应该是`return(baseType.IsGenericType && baseType.GetGenericTypeDefinition()== genericType)|| IsAssignableToGenericType(baseType,genericType);`仅仅因为基类是泛型的,你无法避免递归。 – 2010-09-22 03:53:53

4
if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) { 
+0

使用反射。它的工作原理......但我想知道你是否可以在没有反射的情况下做到这一点,特别是你如何在c#中指定一个非专门的泛型类型。谢谢! – 2008-09-16 17:17:19

+0

我不太确定。我认为运行时在类型系统中有足够的信息能够在不反映程序集的情况下回答IsAssignableFrom问题。 – 2008-09-16 17:27:53

0

我不确定我明白你的意思。你想知道对象是任何通用类型还是你想测试它是否是特定通用类型?或者你只是想知道是否可枚举?

我不认为第一个可能。第二个绝对有可能,只是把它当作其他类型。对于第三种,只需按照您的建议对IEnumerable进行测试即可。

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

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

查看this question about types了解更多详情。也许它会帮助你。

+0

我想知道在我的例子中V是否实现了IEnumerable的任何东西,我不在乎什么。 我应该说的项目[关键]是IEnumerable 在我的例子。感谢您接受这一点。 – 2008-09-16 17:32:57

1

我会使用重载:

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

public static void DoSomething<K,V>(IDictionary<K,V> items) 
{ 
    items.Keys.Each(key => { /* do something else */ }); 
} 
5

警告有关泛型类型和使用IsAssignableFrom()的一个字......

假设你有以下几点:

public class MyListBase<T> : IEnumerable<T> where T : ItemBase 
{ 
} 

public class MyItem : ItemBase 
{ 
} 

public class MyDerivedList : MyListBase<MyItem> 
{ 
} 

呼吁基本列表类型或派生列表类型IsAssignableFrom会返回假,但明确MyDerivedList继承MyListBase<T>。 (Jeff的一个快速注释,绝对的必须被包装在一个代码块或tildes得到<T>,否则它被忽略。这是故意的吗?)这个问题源于这样一个事实,即MyListBase<MyItem>被视为一个完全不同的类型MyListBase<T>。下面的文章可以更好地解释这一点。 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); 
    } 

/编辑:这需要通用递归考虑以及接口是当场就康拉德的新职位。非常好的工作。 :)

/EDIT2:如果检查genericType是否为接口,可以实现性能优势。该检查可以是一个,如果围绕当前界面的代码块,但如果你有兴趣使用.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); 
    } 
0

我曾经认为这种情况可能是可解类似@Thomas Danecker的solution一种方式,但增加一个模板参数:

public static void DoSomething<K, V, U>(IDictionary<K,V> items) 
    where V : IEnumerable<U> { /* do something */ } 
public static void DoSomething<K, V>(IDictionary<K,V> items) 
          { /* do something else */ } 

但我现在发现,但这并没有工作,除非我明确地指定了第一种方法的模板参数。这显然不是根据字典中的每个项目定制的,但它可能是一种穷人的解决方案。

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

82

非常感谢这篇文章。我想提供康拉德鲁道夫解决方案的一个版本,这对我来说效果更好。

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); 
} 
4

感谢伟大的信息:测试时,如果A型为空值类型我有小问题与该版本,尤其是。为了方便起见,我将它重构为扩展方法,并将其简化为单个语句。

sometype。这时候:

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)); 
} 

现在,它可以很容易地与调用。IsAssignableToGenericType(typeof运算(MyGenericType <>))