为什么要将 AsEnumerable() 方法应用于数组

本文关键字:方法 应用于 数组 AsEnumerable 为什么 | 更新日期: 2023-09-27 17:50:49

我正在阅读C# AsEnumerable:

"IEnumerable 接口是一个通用接口。这意味着它 定义类型可以实现循环的模板。这 AsEnumerable 方法是一种泛型方法,允许您强制转换特定的 类型到其 IEnumerable 等效项">

再进一步,一个代码示例:

using System;
using System.Linq;
class Program
{
   static void Main()
   {
       // Create an array type.
        int[] array = new int[2];
        array[0] = 5;
        array[1] = 6;
        // Call AsEnumerable method.
        var query = array.AsEnumerable();
        foreach (var element in query)
        {
            Console.WriteLine(element);
        }
    }
}

听起来我需要将数组转换为 IEnumerable 类型对象才能使用循环(foreach

但是将foreach直接应用于数组会产生完全相同的结果:

using System;
//using System.Linq;
class Program
{
    static void Main()
    {
        // Create an array type.
        int[] array = new int[2];
        array[0] = 5;
        array[1] = 6;
        // Call AsEnumerable method.
        //var query = array.AsEnumerable();
        foreach (var element in array)
        {
            Console.WriteLine(element);
        }
    }
}

因此,带有AsEnumerable((方法解释的整个网页对我来说是无效的。
我错过了什么?

为什么要将 AsEnumerable() 方法应用于数组

这个例子很糟糕,应该感觉很糟糕。这是一个更好的,如果有点做作的例子:

如果我在数组类型上定义了扩展方法,如下所示:

public static class ArrayExtension {
    public static bool Any<T>(this T[] source, Func<T,bool> predicate)
    {
       Console.WriteLine("Undesirable side behaviour");
       SomeResourceIntensiveOperation();
       Console.WriteLine("Inefficient implementation");
       return source.Where(predicate).Count() != 0;
    }
}

我做

int[] nums = new []{1,2,3,4,5};
nums.Any(n=> n % 2 == 0);

如果将执行并运行我的实现,即使我不需要它。通过做

nums.AsEnumerable().Any(n => n % 2 == 0);

它将调用默认实现。

真正的好处是当你使用IQueryable实现(例如LINQ-to-SQL(时,因为,例如,IEnumerable的Where被定义为

public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate) 

IQueryable.Where定义为

public static IQueryable<TSource> Where<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate)

当智商行为不受欢迎时,可以调用AsEnumerable()来强制 IEnumerable 行为。

来自MSDN

AsEnumerable<TSource> (IEnumerable<TSource>( 方法除了将源代码的编译时类型从实现IEnumerable<T>的类型更改为IEnumerable<T>本身之外没有任何效果。

AsEnumerable<TSource>(IEnumerable<TSource>(可用于在序列实现IEnumerable<T>时在查询实现之间进行选择,但也具有一组不同的公共查询方法可用。例如,给定一个泛型类 Table 实现 IEnumerable<T> 并有自己的方法,如 WhereSelectSelectMany,对 Where 的调用将调用 Table 的公共Where方法。表示数据库表的 Table 类型可以具有一个 Where 方法,该方法将谓词参数作为表达式树,并将树转换为 SQL 以进行远程执行。如果不需要远程执行(例如,因为谓词调用本地方法(,则可以使用 AsEnumerable<TSource> 方法来隐藏自定义方法,而是使标准查询运算符可用。

在你的例子中逻辑上是没有意义的(即来自数组(。我假设第一个代码是由初学者编写的,或者 - 更深入 - 一个示例。

在 LINQ 的意义上,它确实有意义,因为"AsEnumerable"会触发查询的评估,并且取决于 ORM 这可能意味着释放数据库连接以便在循环中重用。

可是:

你读了太多的例子。在一个例子中,代码不是为了"好",而是为了显示一个观点。在这种情况下,演示AsEnumerable的使用可能是有意义的 - 并且Array是初始化的最快可枚举对象(就代码行而言(,以保持示例简短。示例指出了具体的事情,它们不是任何东西的"好代码"。

这只是另一个例子。假设我有这个方法:

static void MyMeth(int[] numbers)
{
  var query = numbers.Reverse();  // works fine, calls Linq extension
  // ... use query ...
}

然后我决定将numbers改为List<int>,并尝试:

static void MyMeth(List<int> numbers)
{
  var query = numbers.Reverse();  // will not compile!
  // ... use query ...
}

这里的问题是List<>类有另一个方法,也称为Reverse。该方法返回void(因为它就地修改原始List<>(。我不想这样。一种解决方案是显式地numbers

static void MyMeth(List<int> numbers)
{
  var query = ((IEnumerable<int>)numbers).Reverse();  // fine; Linq
  // ... use query ...
}

但另一种解决方案是AsEnumerable<>,所以:

static void MyMeth(List<int> numbers)
{
  var query = numbers.AsEnumerable().Reverse();  // fine too; Linq
  // ... use query ...
}

结论:AsEnumerable方法的目的是"忘记"专用类型上碰巧"隐藏"通用类型IEnumerable<>上的扩展方法的方法。在"专用"类型是/继承的情况下,这可能非常重要IQueryable<>有(扩展(方法WhereSelect等,它们做一些不同的事情(即将lambda作为表达式树摄取,分析它,并将其"翻译"为SQL或其他东西(而不是IEnumerable<> WhereSelect等。