C# Interface IEnumerable Any() 没有指定泛型类型

C# Interface IEnumerable Any() without specifying generic types

我投了

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 Query Any() 来查看此可枚举对象是否包含任何元素。但不幸的是,即使使用 Linq,我也做不到。

如果不指定通用类型,我将如何执行此操作。

一种方法是使用foreach, as noted in IEnumerable "Remarks"。它还提供了有关 GetEnumerator 结果的其他方法的详细信息。

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

(它本身很容易转移到扩展方法。)

虽然你不能直接这样做,但你可以通过 Cast:

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

这应该总是可行的,因为任何 IEnumerable 都可以包装为 IEnumerable<object>。如果它是 actually 一个 IEnumerable<int> 或类似的,它最终会装箱第一个元素,但它应该可以正常工作。与大多数 LINQ 方法不同,CastOfType 目标是 IEnumerable 而不是 IEnumerable<T>.

当然,您可以编写自己的扩展方法子集,例如 LINQ 方法,但如果您愿意,可以在非泛型 IEnumerable 类型上运行。实施 LINQ to Objects 并不难 - 例如,您可以使用我的 Edulinq project 作为起点。

在某些情况下,您可以比使用 Cast 更有效地实现 Any(IEnumerable) - 例如,如果目标实现非通用 ICollection 接口,则采取快捷方式。那时,您不需要创建迭代器或获取第一个元素。在大多数情况下,这不会产生太大的性能差异,但如果您正在优化,您 可以 做这种事情。

您尝试使用 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);
}