测试一个类型是否为集合的最佳方法

7

我有一个方法,接受类型为System.Object的参数obj

现在我想检查obj的实际类型是否为:

  • 集合类型(IEnumerable)。
  • 其他任何类型。

我想到的第一种方法是:

if (obj is IEnumerable) 
   // obj is a collection

但是 System.String 实现了 IEnumerable 接口,我不想把字符串视为集合

我想到的第二种方法是测试 ICollection 而不是 IEnumerable,因为 IEnumerable 更多的是潜在的集合而不是实际的集合。这将排除字符串,但也会排除不继承 ICollection 的 ICollection-Of-T,因为 IEnumerable-Of-T 是唯一向后兼容的泛型集合抽象 - 它继承自 IEnumerable。

所以我想最好的方法是:

if (obj is string) 
  // not a collection
else if (obj is IEnumerable) 
  // collection
else
  // not a collection

有更好的方法吗?

我将可枚举对象视为序列,而不是集合。这里可能存在微妙的意义差异;因为我不知道你的应用程序,所以我不知道这是否相关。在设计集合初始化器时,我们面临了这个问题。有许多“集合”没有实现ICollection,而有许多实现Add的对象不是集合(例如Math对象)。我们决定使用一种启发式方法。如果一个类型既实现IEnumerable又有一个公共的Add方法,它就很可能是一个集合。这可能有点笨拙,但在实践中它是有效的。 - Eric Lippert
这是一个关于集合抽象的好资源: http://blogs.msdn.com/brada/archive/2005/01/18/355755.aspx - Max Toro
3个回答

10

我认为你有点过于复杂化了。如果你真的想使用IEnumerable但是排除System.String,那为什么不直接在代码中实现呢?

public static bool IsCollection(object obj) {
  return obj is IEnumerable && !(obj is String);
}

1
这似乎是唯一的方法。我认为当BCL团队设计String类型时,他们没有意识到IEnumerable作为引用任何集合的唯一方式会变得如此重要。 - Max Toro

5

如果您只是想进行测试:

bool isCollection = obj.GetType().GetInterfaces()
    .Any(iface => iface.GetGenericTypeDefinition() == typeof(ICollection<>))

但是,坦白地说,如果你只想特殊处理字符串(顺便问一下,为什么要这样做?),那就直接这样做吧。如果你测试的是 ICollection<>,那么你将把 LINQ 查询的结果视为“非集合”,但没有充分的理由。


或者 obj.GetType().GetInterface("System.Collections.IEnumerable") != null - xr280xr

-1
如果您想要检查并在包括可空类型的任何列表/集合/IEnumerable中获得true,但对于字符串类型获得false,则可以这样做:
   private static bool IsIEnumerable(Type requestType)
   {
      var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(requestType);
      var notString = !typeof(string).IsAssignableFrom(requestType);
      return isIEnumerable && notString;
   }

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