如何确定一个类型是否是集合类型?

52
我试图确定一个运行时类型是否是某种集合类型。我下面的代码可以工作,但是我觉得很奇怪,我必须像我所做的那样在数组中命名我认为是集合类型的类型。
在下面的代码中,通用逻辑的原因是因为在我的应用程序中,我希望所有集合都是通用的。
bool IsCollectionType(Type type)
{
    if (!type.GetGenericArguments().Any())
        return false;

    Type genericTypeDefinition = type.GetGenericTypeDefinition();
    var collectionTypes = new[] { typeof(IEnumerable<>), typeof(ICollection<>), typeof(IList<>), typeof(List<>) };
    return collectionTypes.Any(x => x.IsAssignableFrom(genericTypeDefinition));
}

如何对这段代码进行重构,使其更加智能或简单?


需要记住的一件事是,即使string实现了IEnumerable<char>,通常情况下你不应该将其视为char的集合。 - svick
5个回答

97

实际上,这些类型都继承自IEnumerable。您只需要检查它即可:

bool IsEnumerableType(Type type)
{
    return (type.GetInterface(nameof(IEnumerable)) != null);
}

或者如果你真的需要检查是否为 ICollection:

bool IsCollectionType(Type type)
{
    return (type.GetInterface(nameof(ICollection)) != null);
}

看一下" Syntax "部分:

如果您需要排除字符串(它们本质上是IEnumerable<char>),请使用以下函数:

bool IsEnumerableType(Type type)
{
    return (type.Name != nameof(String) 
        && type.GetInterface(nameof(IEnumerable)) != null);
}

1
文档中的继承层次结构并不会告诉你实现的接口,但查看语法部分会有所帮助。 - svick
1
@Ruben,这正是我所说的,但这并不在继承层次结构部分。 - svick
25
检查 IEnumerable 存在将 string 错误解释为集合的问题,这在大多数情况下是不可取的。需要避免这种情况。 - Mohammad Dehghan
2
完全没有用。在 type.GetInterface("ICollection")type.GetInterface("System.Collections.Generic.ICollection") 上返回了 null,并且返回了 ICollection<CPerson> - Roman
请小心处理此事,因为字符串也实现了IEnumerable。 - Igneous01
显示剩余7条评论

4

您可以使用此助手方法来检查类型是否实现了开放式泛型接口。在您的情况下,您可以使用 DoesTypeSupportInterface(type, typeof(Collection<>))

public static bool DoesTypeSupportInterface(Type type,Type inter)
{
    if(inter.IsAssignableFrom(type))
        return true;
    if(type.GetInterfaces().Any(i=>i. IsGenericType && i.GetGenericTypeDefinition()==inter))
        return true;
    return false;
}

或者你可以简单地检查非通用 IEnumerable。所有集合接口都继承自它。但我不会将任何实现 IEnumerable 的类型称为集合。


或者使用在此处找到的解决方案(https://dev59.com/5nVD5IYBdhLWcg3wI3-L#1075059),该解决方案适用于通用类型以及通用接口。 - HappyNomad

1
这个解决方案将处理ICollectionICollection<T>
    static bool IsCollectionType(Type type)
    {
        return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && (s.Name == "ICollection" || s.Name.StartsWith("ICollection`")));
    }

1
你可以使用LINQ,搜索接口名称,例如:
yourobject.GetType().GetInterfaces().Where(s => s.Name == "IEnumerable")

如果这个值是 IEnumerable 的实例。

4
所以会导致误报的字符串 - War
字符串是一个字符集合,因此它不是误报。也许这不是你要找的。 - edc65

0

这对我有效。

private static bool IsCollectionType(Type type)
{
    return type.GetInterfaces().Any(s => s.Namespace == "System.Collections.Generic" && (s.Name == "IEnumerable" || s.Name.StartsWith("IEnumerable`")));
}

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