如何检测类型是否为另一种通用类型 [英] How To Detect If Type is Another Generic Type

查看:78
本文介绍了如何检测类型是否为另一种通用类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

示例:

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吗?

Can this be done without using reflection? How do I say IEnumerable in C#? Should I just use IEnumerable since IEnumerable<> implements IEnumerable?

推荐答案

先前接受的答案不错,但是它是错误的。幸运的是,该错误很小,正在检查 IEnumerable 是不够的;有很多类仅实现非通用接口,我将在稍后给出答案。要指出的是,可接受的答案过于复杂,因为以下代码会d在给定的情况下达到相同的效果:

The previously accepted answer is nice but it is wrong. Thankfully, the error is a small one. Checking for IEnumerable is not enough if you really want to know about the generic version of the interface; there are a lot of classes that implement only the nongeneric interface. I'll give the answer in a minute. First, though, I'd like to point out that the accepted answer is overly complicated, since the following code would achieve the same under the given circumstances:

if (items[key] is IEnumerable)

这还可以做得更多,因为它分别适用于每个项目(不适用于它们的常见子类 V )。

This does even more because it works for each item separately (and not on their common subclass, V).

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

Now, for the correct solution. This is a bit more complicated because we have to take the generic type IEnumerable`1 (that is, the type IEnumerable<> with one type parameter) and inject the right generic argument:

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

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

You can test the correctness of this code easily:

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

收益率:

True
False

不要过分担心以下事实这需要反射。的确,这确实增加了运行时开销,使用 is 运算符也是如此。

Don't be overly concerned by the fact that this uses reflection. While it's true that this adds runtime overhead, so does the use of the is operator.

当然,上面的代码受到严格限制,可以扩展为更通用的方法 IsAssignableToGenericType 。以下实现有些不正确 1 ,我将其留在这里仅出于历史目的请勿使用。取而代之的是, James在他的回答中提供了出色而正确的实现。

Of course the above code is awfully constrained and could be expanded into a more generally applicable method, IsAssignableToGenericType. The following implementation is slightly incorrect1 and I’ll leave it here for historic purposes only. Do not use it. Instead, 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);
}

1 genericType givenType 相同;由于相同的原因,它对于可为空的类型也会失败,即

1 It fails when the genericType is the same as givenType; for the same reason, it fails for nullable types, i.e.

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

我已经创建了要点,其中包含一整套测试用例

这篇关于如何检测类型是否为另一种通用类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆