C# 检查泛型IEnumerable是否为空

C# 检查泛型IEnumerable是否为空,c#,linq,generics,C#,Linq,Generics,假设我有一个对象,它的类型可能是IEnumerable。我想编写一个方法,如果对象的类型为IEnumerable、不为null且不为空,则返回true 以下是到目前为止我得到的信息: public bool IsNullOrEmpty(object obj) { if (obj != null) { if (obj is IEnumerable<object>) { return (obj as IEnumera

假设我有一个对象,它的类型可能是
IEnumerable
。我想编写一个方法,如果对象的类型为
IEnumerable
、不为null且不为空,则返回true

以下是到目前为止我得到的信息:

public bool IsNullOrEmpty(object obj)
{
    if (obj != null)
    {
        if (obj is IEnumerable<object>)
        {
            return (obj as IEnumerable<object>).Any();
        }
    }
    return false;
}
public bool为空或空(object obj)
{
如果(obj!=null)
{
if(对象是IEnumerable)
{
return(对象为IEnumerable).Any();
}
}
返回false;
}
如果传入类型为
List
的对象,则此操作有效,但如果传入类型为
List
的对象,则此操作无效。它失败是因为
obj是IEnumerable
返回
false

你知道我如何使所有的泛型
IEnumerable
s都可以这样做吗?

继承自-因此,如果你可以检查非泛型
IEnumerable
,而不是泛型
IEnumerable
,你可以直接转换到
IEnumerable

关于代码的几点注意事项:首先用
检查is
,然后用
转换为
。这通常是不必要的
as
已经检查,如果强制转换失败,则返回
null
。因此,较短的方法是:

var enumerable = obj as IEnumerable;
if (enumerable != null) {
    return !enumerable.Cast<object>().Any();
}
var enumerable=obj作为IEnumerable;
if(可枚举!=null){
return!enumerable.Cast().Any();
}

请注意,您需要对那里进行额外的调用,因为需要一个通用的
IEnumerable

由于类型可能未知,您可以尝试检查
IEnumerable
接口,并在枚举器上使用
MoveNext()

编辑:我更新了方法名称。由于原始问题代码正在检查集合中是否有项,因此现在使用逻辑更有意义

public bool IsNotNullOrEmpty(object enumerable)
{
    if (enumerable != null)
    {
        if (enumerable is IEnumerable)
        {
            using(var enumerator = ((IEnumerable)enumerable).GetEnumerator())
                return enumerator.MoveNext();
        }
    }
    return false;
}

您可以尝试将其强制转换为
IEnumerable

public static bool IsNullOrEmpty<T>(this T obj) where T : class
{
    if (obj == null) return true;
    IEnumerable seq = obj as IEnumerable;
    if (seq != null) return !seq.Cast<object>().Any();
    return false;
}

您可以检查非泛型
IEnumerable
并检查其是否为空。您可以添加检查以确保对象使用反射实现
IEnumerable

public static bool IsNullOrEmpty(object obj)
{
    var e = obj as System.Collections.IEnumerable;
    if (e == null || !e.GetType().GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))) return false;

    foreach (object _ in e)
    {
        return true;
    }
    return false;
}
publicstaticbool为空或空(objectobj)
{
var e=对象作为System.Collections.IEnumerable;
如果(e==null | |!e.GetType().GetInterfaces().Any(i=>i.IsGenericType&&i.GetGenericTypeDefinition()==typeof(IEnumerable))返回false;
foreach(对象在e中)
{
返回true;
}
返回false;
}

您可以首先检查对象是否实现了与列表和数组类似的
ICollection
,因为检查其中一个的大小比较便宜,因为它们具有
Count
属性。如果不是,您可以检查它是否实现了
IEnumerable
,如果实现了,请创建一个枚举器,看看是否可以移动到第一项:

public static bool IsNullOrEmpty(object obj) {
  ICollection c = obj as ICollection;
  if (c != null) {
    return c.Count == 0;
  }
  IEnumerable e = obj as IEnumerable;
  if (e != null) {
    return !e.GetEnumerator().MoveNext();
  }
  return false;
}

这回答了将问题扩展到
IEnumerable
范围之外的问题,即“我能在
foreach
-循环中使用这个东西吗?”

您可能知道,
foreach
主要不检查
IEnumerable
,而是尝试枚举给定的内容

这将适用于在某种意义上可枚举的所有类型,
foreach
认为:

public bool IsNullOrEmpty(object obj)
{
  if (obj != null) // we can't be null
  {
    var method = obj.GetType().GetMethod("GetEnumerator");
    if (method != null) // we have an enumerator method
    {
      var enumerator = method.Invoke(obj, null);
      if (enumerator != null) // we got the enumerator
      {
        var moveMethod = enumerator.GetType().GetMethod("MoveNext")
        if (moveMethod != null) // we have a movenext method, lets try it
          return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
      }
    }
  }
  return true; // it's empty, lets return true
}
这基本上与foreach-循环相同,即检查可枚举性,并不真正说明所涉及的类型


因此,确实应该避免这种情况,但如果您必须检查是否可以将键入的内容放入
foreach
-循环,则应该可以做到这一点。

为完整起见,请为最新的c版本(来自c 8.0)添加最先进的答案:


坦率地说,“修复”这个问题是个坏主意;在一般情况下,您不应假设
IEnumerable[]
是可重复的,在这种情况下:询问
。Any()
可能意味着您永远无法询问实际数据如果它是包含一个元素的
列表,
false
这不会破坏函数吗?如果你询问枚举是否为空,你不应该返回
Any()
的否定结果吗?@DakotahHicock No-
Any
没有lambda参数,它不会尝试计算内容-它只会检查是否存在任何成员。@marcgravel:,对
any()
的任何调用都是如此,不管是在这里显示的方法之内还是之外。你不能在
对象上调用它。
@RobertHarvey true,我没有意识到类型事先不知道。我将尝试并更新。您的方法不会尝试
处理
使用
GetEnumerator
创建的
IEnumerator
enumerable是IEnumerable
实际上已经执行了空检查,因此如果(enumerable!=null),您可以删除该行
(:如果提供的表达式为非null,则is表达式的计算结果为true,并且可以将提供的对象强制转换为提供的类型,而不会引发异常。)我不知道您可以将扩展方法附加到任意的
t
类型。该扩展方法会出现在任何类中吗?为什么这应该是泛型方法?您没有在方法中使用type
t
,并且由于您没有尝试将
null
分配给
t
类型的任何标识符,因此您没有使用任何@O.R.Mapper:为什么允许它在
int
这样的值类型上使用它?使用类约束需要是泛型的。@RobertHarvey:仅用于引用类型。因此,您可以
“”.IsNullOrEmpty()
但不能
1.IsNullOrEmpty()
@TimSchmelter:为什么不允许它在
int
这样的值类型上使用它呢?你不想
MoveNext
调用后处理
IEnumerator
,如果它是
IDisposable
foreach
总是这样做的。@NathanA:
s/IEnumerable/IEnumerator//code>?@NathanA-I也可以,但非泛型的
IEnumerator
n
public static bool IsNullOrEmpty(object obj) {
  ICollection c = obj as ICollection;
  if (c != null) {
    return c.Count == 0;
  }
  IEnumerable e = obj as IEnumerable;
  if (e != null) {
    return !e.GetEnumerator().MoveNext();
  }
  return false;
}
public bool IsNullOrEmpty(object obj)
{
  if (obj != null) // we can't be null
  {
    var method = obj.GetType().GetMethod("GetEnumerator");
    if (method != null) // we have an enumerator method
    {
      var enumerator = method.Invoke(obj, null);
      if (enumerator != null) // we got the enumerator
      {
        var moveMethod = enumerator.GetType().GetMethod("MoveNext")
        if (moveMethod != null) // we have a movenext method, lets try it
          return !(bool)moveMethod.Invoke(enumerator, null); // ie true = empty, false = has elements
      }
    }
  }
  return true; // it's empty, lets return true
}
static bool IsNotNullOrEmpty<T>(object obj) => obj is IEnumerable<T> seq && seq.Any();
static bool IsNullOrEmpty<T>(object obj) => !(obj is IEnumerable<T> seq && seq.Any());
static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();