不指定泛型类型的C#接口IEnumerable Any()

5

I have casted

var info = property.Info;
object data = info.GetValue(obj);

...

var enumerable = (IEnumerable)data;

if (enumerable.Any())  ///Does not compile
{
}

if (enumerable.GetEnumerator().Current != null) // Run time error
{
}

我希望通过使用Linq查询Any()方法来检查此可枚举对象是否有任何元素。但是,即使使用了Linq,我也无法做到这一点。

如果不指定泛型类型,我该如何实现这个功能呢?

3个回答

25

虽然您不能直接执行此操作,但可以通过 Cast 来执行:

if (enumerable.Cast<object>().Any())

这应该总是有效的,因为任何 IEnumerable 都可以被封装为一个 IEnumerable<object>。如果第一个元素实际上是 IEnumerable<int> 或类似类型,则会进行装箱,但它应该可以正常工作。与大多数 LINQ 方法不同,CastOfType 是针对 IEnumerable 而不是 IEnumerable<T>

当然,如果你愿意,你可以编写自己的扩展方法子集,就像 LINQ 一样,但操作非泛型的 IEnumerable 类型。实现 LINQ to Objects 并不是非常困难——例如,你可以使用我的 Edulinq 项目 作为起点。

有些情况下,你可以比使用 Cast 更高效地实现 Any(IEnumerable)——例如,如果目标实现了非泛型的 ICollection 接口,则可以采用捷径。此时,你就不需要创建迭代器或取第一个元素。在大多数情况下,这不会产生太大的性能差异,但如果你正在优化,这是你可以做的事情。


2

一种方法是使用foreach,如在IEnumerable“备注”中所述。它还提供了有关GetEnumerator结果的附加方法的详细信息。

bool hasAny = false;
foreach (object i in (IEnumerable)(new int[1] /* IEnumerable of any type */)) {
    hasAny = true;
    break;
}

(这本身很容易转换为扩展方法。)

这真的是唯一的方法吗?令人惊讶。看起来像一个大的hack。 - Ilan Keshet
@IlanKeshet 不是“唯一”的方法。考虑到IEnumerable接口的暴露,这也不是真正的“hack”。 - user2864740
肯定不是黑客,这是一种枚举IEnumerable并检查各种条件的有效方法。 - Mrinal Kamboj

2
你尝试使用 GetEnumerator().Current,试图获取尚未移动到第一个位置的枚举器的当前值。如果第一个项目存在或为空,则也会给出错误的结果。你可以做的是(以及 Enumerable 中的 Any() 所做的)查看是否有可能移动到第一个项目;即是否有第一个项目可供移动:
internal static class UntypedLinq
{
    public static bool Any(this IEnumerable source)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        IEnumerator ator = source.GetEnumerator();
        // Unfortunately unlike IEnumerator<T>, IEnumerator does not implement
        // IDisposable. (A design flaw fixed when IEnumerator<T> was added).
        // We need to test whether disposal is required or not.
        if (ator is IDisposable disp)
        {
            using(disp)
            {
                return ator.MoveNext();
            }
        }

        return ator.MoveNext();
    }

    // Not completely necessary. Causes any typed enumerables to be handled by the existing Any
    // in Linq via a short method that will be inlined.
    public static bool Any<T>(this IEnumerable<T> source) => Enumerable.Any(source);
}

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