如何判断一个类型是否实现了IList<>接口?

21

我想编写一个使用反射来判断给定类型是否实现了 IList<T> 的方法。例如:

IsGenericList(typeof(int))                       // should return false
IsGenericList(typeof(ArrayList))                 // should return false
IsGenericList(typeof(IList<int>))                // should return true
IsGenericList(typeof(List<int>))                 // should return true
IsGenericList(typeof(ObservableCollection<int>)) // should return true

就我的使用经验来说,我可以假设类型总是一个实例化的泛型类型(或者根本不是泛型的东西)。

不幸的是,这并不像应该那么简单。显而易见的解决方案:

public bool IsGenericList(Type type)
{
    return typeof(IList<>).IsAssignableFrom(type);
}

代码不起作用,它总是返回false。显然,像 IList<> 这样的未实例化泛型类型不能按照我所期望的方式实现 IsAssignableFrom: IList<> 无法从 List<T> 赋值。

我还尝试了这个:

public bool IsGenericList(Type type)
{
    if (!type.IsGenericType)
        return false;
    var genericTypeDefinition = type.GetGenericTypeDefinition();
    return typeof(List<>).IsAssignableFrom(genericTypeDefinition);
}

即,将type转换为其未实例化的泛型类型,如IList<int>->IList<>,然后再次尝试IsAssignableFrom。当type是已实例化的IList<T>,例如IList<int>IList<object>等时,将返回true。但它对于实现IList<T>的类(如List<int>ObservableCollection<double>等)返回false,因此显然IList<>不能从List<>分配。这与我的预期不同。

我该如何编写IsGenericList并使其像上面的示例一样工作?

8个回答

31

事实上,你不能创建一个泛型类型定义的实例。因此,IsAssignableFrom() 方法按预期工作。要实现你想要的功能,请执行以下操作:

public bool IsGenericList(Type type)
{
    if (type == null) {
        throw new ArgumentNullException("type");
    }
    foreach (Type @interface in type.GetInterfaces()) {
        if (@interface.IsGenericType) {
            if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>)) {
                // if needed, you can also return the type used as generic argument
                return true;
            }
        }
    }
    return false;
}

只是出于好奇,你需要这个做什么?


1
+1 - 这个方案可行,目前这是唯一的解决方案 - 我刚发现你已经提到了这个,之前我也提过同样的建议。 - Reed Copsey
1
好的解决方案。 :) 但是有一个小问题:通常不建议使用@符号。(您可以将变量命名为“i”或“curInterface”以解决名称冲突。) - Noldorin
1
你缺少了方法的 { 符号,并且你使用了 ICollection<> 而不是 IList<>,但除此之外这个代码完美运行,谢谢! - Joe White
1
我在反射类型的字段和属性并将它们重置为默认值(引用类型为null)时使用了这个。对于列表,我想将它们重置为空列表而不是null。 - rohancragg
1
小心@JoeWhite,如果你用IList替换了ICollection,那么IsGenericList(typeof(IList<int>))将返回False。请参考以下修复方法:https://dev59.com/W3NA5IYBdhLWcg3wdtpd#13589104 - Colonel Panic
显示剩余3条评论

18

我也想测试一个类型是否实现了某个T的。我对Lucero的回答做出了明显的修改,但它导致了一个微妙的错误,原始答案中没有这个错误。这是我的最终编辑:

    /// <summary>
    /// Test if a type derives from IList of T, for any T.
    /// </summary>
    public bool TestIfGenericList(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        var interfaceTest = new Predicate<Type>(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>));

        return interfaceTest(type) || type.GetInterfaces().Any(i => interfaceTest(i));
    }

3
谢谢,这的确比被接受的答案更好,因为它适用于IList<T>本身。 - Cœur

2

Lucero/Reed Copsey 现在都有正确的解决方案。为了更加简洁,以下是使用 LINQ 的形式:

var isGenericList = type.GetInterfaces().Any(t => t.IsGenericType && 
    t.GetGenericTypeDefinition() == typeof(IList<>));

无法工作,因为GetInterfaces()不会返回通用类型定义,只会返回通用类型(例如IList<int>)。 - Lucero
是的,它没有。我已经更新了它,现在似乎可以工作了。 - Noldorin
如果你想的话,可以使用Linq重写我的解决方案... ;) - Lucero
我没有选择Linq,因为从问题中只清楚地知道泛型是可用的,而不知道正在使用哪个版本的C#编译器。 - Lucero
它在第三个案例中再次失败了...看看我的解决方案-更短,更快。 - tanascius
Lucero的解决方案似乎更加健壮,因为它不涉及字符串。 - Noldorin

2
使用以下方法:http://msdn.microsoft.com/en-us/library/system.type.findinterfaces.aspx 我尝试了这个:
 public class Test : IList<string>
 {
//implementation left out...
 }

class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            TypeFilter myFilter = new TypeFilter(MyInterfaceFilter);

            Type type = t.GetType();
            Type[] x = type.FindInterfaces(myFilter, "System.Collections.Generic.IList");
            Console.WriteLine(x.Length);

        }

        public static bool MyInterfaceFilter(Type typeObj, Object criteriaObj)
        {
            if (typeObj.ToString().Contains(criteriaObj.ToString()))
                return true;
            else
                return false;
        }
    }

6
使用非强名称来标识类型非常不安全。任何人都可以创建一个具有该名称的类型,这并不意味着它就是您想要测试的类型。 - Lucero
当然,你可以用以下代码替换它: Type[] x = type.FindInterfaces(myFilter, typeof(IList<>).FullName); - Irwin

1

这个通过了你的测试...

public static bool IsGenericList( Type type )
{
  return type.Name == "IList`1" || type.GetInterface( "IList`1" ) != null;
}

类型不必是通用的才能实现通用接口。你可以这样做:class IntList: object, IList<int> { .... } 这并不使IntList成为通用类型。 - Lucero
IsGeneric检查根本不必要。 - tanascius

1

我喜欢Colonel Panic的回答,但我决定将答案变得更通用,这样我就可以在其他情境下重复使用它:

    public static bool InstanceOfGenericType(this Type self, Type genericType)
    {
        return self.IsGenericType && self.GetGenericTypeDefinition() == genericType;
    }

    public static bool InstanceOfGenericInterface(this Type self, Type ifaceType)
    {
        return self.InstanceOfGenericType(ifaceType) 
            || self.GetInterfaces().Any(i => i.InstanceOfGenericType(ifaceType));
    }

    public static bool IsIList(this Type t)
    {
        return t.InstanceOfGenericInterface(typeof(IList<>));
    }

0

你尝试过调用Type.GetInterface()吗?从帮助文档中并不完全清楚,但我认为它会搜索基类型实现的接口,除了类型本身。如果不行,你可以始终循环遍历Type.BaseType并再次调用GetInterface()


GetInterface 只有在您知道特定的泛型参数时才能工作,但对于 IList<T> 不起作用。 - Reed Copsey

-3
使用 "is" 运算符:

is 运算符用于检查对象的运行时类型是否与给定类型兼容。


2
我没有实例,我有类型。 - Joe White
1
这个不能用在IList<>上,只能用在IList<int>等有具体类型的列表上。你需要为"is"提供一个具体类型才能运行... - Reed Copsey
你需要一个 is 的实例。OP 问如何使用 Type 进行检查。 - Oliver

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