查找一个类型是否实现了一个通用接口

57
假设我有一个类型 MyType。我想要做到以下两点:
  1. 找出 MyType 是否实现了 IList 接口,用于某个 T。
  2. 如果第一点的答案是肯定的,找出 T 是什么。
似乎可以使用 GetInterface() 方法来完成此操作,但是它只能让您通过特定名称进行搜索。是否有一种方法可以搜索“所有形式为 IList 的接口”(如果可能,如果接口是 IList 的子接口,则这种方法也很有用)。
相关问题:如何确定类型是否实现了特定的泛型接口类型
7个回答

89
// this conditional is necessary if myType can be an interface,
// because an interface doesn't implement itself: for example,
// typeof (IList<int>).GetInterfaces () does not contain IList<int>!
if (myType.IsInterface && myType.IsGenericType && 
    myType.GetGenericTypeDefinition () == typeof (IList<>))
    return myType.GetGenericArguments ()[0] ;

foreach (var i in myType.GetInterfaces ())
    if (i.IsGenericType && i.GetGenericTypeDefinition () == typeof (IList<>))
        return i.GetGenericArguments ()[0] ;

编辑:即使myType实现了IDerivedFromList<>但不是直接实现IList<>IList<>仍然会在由GetInterfaces()返回的数组中显示。

更新:添加了一项检查,针对myType是所讨论的泛型接口的边缘情况。


这也处理了数组的情况,这很好。如果您想显式地测试数组,则使用“if(myType.IsArray)return myType.GetElementType();”。(虽然这可能更快,但我希望这些都不是性能关键!) - yoyo
对于像我这样好奇为什么需要 .IsInterface 的人来说:如果在非泛型类型上调用 GetGenericTypeDefinition(),它会抛出异常。 - GameFreak
Type.IsGenericType属性在netstandard 1.6及以下版本中不可用(因此在.NET Core 1.0上也不可用),但是您可以改用TypeInfo.IsGenericType:type.GetTypeInfo().IsGenericType。 - dotarj
如果MyType是一个开放的泛型类型,那么你还需要调用GetGenericTypeDefinitiin吗?换句话说,为什么typeof(Dictionary<,>).GetInterfaces().Contains(typeof(IDictionary<,>))会返回false? - Daniel Arant
因为您无法实现一个开放的泛型接口。Dictionary<,> 实现了 IDictionary<0, 1>,其中 '0'1 指的是 Dictionary<,> 的类型参数 - 在 ILDasm 或类似工具中查找。 - Anton Tykhyy

11

使用反射(以及一些LINQ)可以很容易地实现此操作:

public static IEnumerable<Type> GetIListTypeParameters(Type type)
{
    // Query.
    return
        from interfaceType in type.GetInterfaces()
        where interfaceType.IsGenericType
        let baseInterface = interfaceType.GetGenericTypeDefinition()
        where baseInterface == typeof(IList<>)
        select interfaceType.GetGenericArguments().First();
}

首先,你获取类型上的接口,并过滤出那些属于泛型类型的接口。

然后,获取这些接口类型的泛型类型定义,并检查它是否与 IList<> 相同。

从那里开始,就是简单地获取原始接口的泛型参数。

请记住,一个类型可能具有多个 IList<T> 实现,这就是为什么返回 IEnumerable<Type> 的原因。


如果你将返回表达式用括号包起来,并在末尾添加另一个“.First()”,那么它将返回一个类型而不是长度为1的IEnumerable<Type>,这样处理起来会更容易一些。 (个人认为这是LINQ过于聪明的一个例子,但也许只是我自己觉得。) - yoyo
@yoyo 或者,你可以直接在此方法的结果上调用 First。如果你从此方法返回 First,那么你就是在假设只有一个类型参数,这是错误的 - casperOne
@casperOne 说得好,OP的MyType可以实现IList<Foo>和IList<Bar>。所以问题应该是“找到T们”,而不是“找到T”。被接受的答案也没有解决这个问题。 - yoyo

4
    public static bool Implements<I>(this Type type) where I : class
    {
         if (!typeof(I).IsInterface)
         {
             throw new ArgumentException("Only interfaces can be 'implemented'.");
         }

         return typeof(I).IsAssignableFrom(type);
    }

2

使用Anton Tykhyy的建议,这里提供一个小的扩展方法来检查某个类型是否实现了具有一个给定泛型类型参数的通用接口:

public static class ExtensionMethods
{
    /// <summary>
    /// Checks if a type has a generic interface. 
    /// For example 
    ///     mytype.HasGenericInterface(typeof(IList<>), typeof(int)) 
    /// will return TRUE if mytype implements IList<int>
    /// </summary>
    public static bool HasGenericInterface(this Type type, Type interf, Type typeparameter)
    {
        foreach (Type i in type.GetInterfaces())
            if (i.IsGenericType && i.GetGenericTypeDefinition() == interf)
                if (i.GetGenericArguments()[0] == typeparameter)
                    return true;

        return false;
    }
}

1
作为一个辅助方法扩展。
public static bool Implements<I>(this Type type, I @interface) where I : class  
{
    if(((@interface as Type)==null) || !(@interface as Type).IsInterface)
        throw new ArgumentException("Only interfaces can be 'implemented'.");

    return (@interface as Type).IsAssignableFrom(type);
}

使用示例:

var testObject = new Dictionary<int, object>();
result = testObject.GetType().Implements(typeof(IDictionary<int, object>)); // true!

0
如果我理解你的问题正确,那么这就是你想要做的。如果不是,请进一步解释。
public class MyType : ISomeInterface
{
}

MyType o = new MyType();

if(o is ISomeInterface)
 {
 }

编辑:如果您更改了问题,请注明已编辑,因为现在我的答案看起来不合适。

在这种情况下,这里有一个非常大的LINQ。

            var item = typeof(MyType).GetInterfaces()
                            .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IList<>))
                            .Select(t => t.GetGenericArguments().First())
                            .FirstOrDefault();

if( item != null )
 //it has a type

0

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