检查通用IEnumerable是否为空

时间:2014-09-04 21:03:26

标签: c# linq generics

我们说我有一个可能属于IEnumerable<T>类型的对象。我想写一个方法,如果对象的类型为IEnumerable<T>,非空,并且不为空,则返回true。

这是我到目前为止所得到的:

public bool IsNullOrEmpty(object obj)
{
    if (obj != null)
    {
        if (obj is IEnumerable<object>)
        {
            return (obj as IEnumerable<object>).Any();
        }
    }
    return false;
}

如果我传入类型为List<string>的对象,则会起作用,但如果我传入类型为List<int>的对象则不行。它失败是因为obj is IEnumerable<object>返回false

知道我如何才能使所有通用IEnumerable的工作成为可能吗?

7 个答案:

答案 0 :(得分:10)

由于类型可能未知,您可以尝试检查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;
}

答案 1 :(得分:8)

System.Collections.Generic.IEnumerable<T>继承自System.Collections.IEnumerable - 因此,如果您可以检查非通用IEnumerable,而不是通用IEnumerable<T>,则可以转换为{ {1}}。

关于您的代码的一些注意事项:您首先使用IEnumerable进行检查,然后使用is进行投射。这通常是不必要的;如果演员失败,as已经检查并返回as。因此,更短的方式是:

null

请注意,您需要对Cast进行额外调用,因为Any需要通用var enumerable = obj as IEnumerable; if (enumerable != null) { return !enumerable.Cast<object>().Any(); }

答案 2 :(得分:2)

您可以尝试将其投射到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;
}

...

List<int> list = new List<int>();
bool nullOrEmpty = list.IsNullOrEmpty();  // true

顺便说一句,有趣的是它可以正确使用空字符串:

bool nullOrEmpty = "".IsNullOrEmpty();   // true

答案 3 :(得分:2)

您可以检查非通用IEnumerable并检查是否为空。您可以添加一个检查以确保对象使用反射实现IEnumerable<T>

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;
}

答案 4 :(得分:2)

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

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());

这假设您会在调用站点中知道 T

如果您不知道 T,则不应使用 IEnumerable<object>,而应使用 IEnumerable。像这样:

static bool IsNotNullOrEmpty(object obj) => obj is IEnumerable seq && seq.GetEnumerator().MoveNext();

答案 5 :(得分:1)

您可以先检查对象是否像列表和数组一样实现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;
}

答案 6 :(得分:-1)

这适用于在某种意义上可枚举的所有类型:

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 - 循环相同,即检查可枚举性,并没有真正对所涉及的类型进行说明。