linq 检查通用IE是否为空

mjqavswn  于 2023-10-13  发布在  其他
关注(0)|答案(7)|浏览(131)

假设我有一个对象,它 * 可能 * 是IEnumerable<T>类型。我想写一个方法,如果对象的类型是IEnumerable<T>,则返回true,不为null,也不为空。
这是我目前得到的:

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的工作?

u5rb5r59

u5rb5r591#

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

**编辑:**我更新了方法名称。现在逻辑更有意义了,因为最初的问题代码是检查集合中是否有项目。

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

a8jjtwal2#

System.Collections.Generic.IEnumerable<T>继承自System.Collections.IEnumerable-因此,如果您可以检查非泛型IEnumerable,而不是泛型IEnumerable<T>,则可以直接转换为IEnumerable
关于你的代码的几点注意事项:首先检查is,然后转换为as。这通常是不必要的; as已经检查并在转换失败时返回null。因此,更短的方法是:

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

请注意,您将需要对Cast的额外调用,因为Any需要一个泛型IEnumerable<T>

o4tp2gmn

o4tp2gmn3#

你可以尝试将其转换为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
62lalag4

62lalag44#

您可以检查非泛型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;
}
dluptydi

dluptydi5#

为了完整起见,添加最新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();
vcirk6k6

vcirk6k66#

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

icomxhvb7#

这个答案将问题扩展到了IEnumerable<T>范围之外,即“我拥有的这个东西可以在foreach-loop中使用吗?”“.
您可能知道,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-loop做的是一样的,即检查可枚举性,并没有真正给予一个关于所涉及类型的该死的东西。
因此,它真的应该避免,但如果你以某种方式必须检查,如果你可以把你的类型在foreach-循环,这应该做的伎俩。

相关问题