繁体   English   中英

为什么使用 .AsEnumerable() 而不是转换为 IEnumerable<T> ?

[英]Why use .AsEnumerable() rather than casting to IEnumerable<T>?

IEnumerable<T>的扩展方法之一是.AsEnumerable() 此方法将调用它的可枚举对象转换为IEnumerable<T>的实例。 但是,由于对象必须实现IEnumerable<T>才能应用于此扩展方法,因此转换为IEnumerable<T>只需转换为IEnumerable<T> 我的问题是为什么这种方法存在?

示例:

List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;

在上面的例子中, stringsEnum1stringsEnum2是等价的。 扩展方法的重点是什么?

编辑:作为推论,为什么在转换为IQueryable<T>时有一个.AsQueryable()方法是等效的?

可读性是这里的主要问题。 考虑一下

Table.AsEnumerable().Where(somePredicate)

可读性比

((IEnumerable<TableObject>)Table).Where(somePredicate).

或者想象一下要在 SQL Server 上执行部分查询,而在内存中执行其余部分:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

现在,至于为什么这样的方法有用,请考虑 LINQ to SQL DataContext中的Table示例。 由于Table是一个IQueryable它实现了IEnumerable 当您对此类Table调用Where方法并枚举结果时,将执行最终导致 SQL 语句在 SQL Server 上执行的代码。 什么AsEnumerable确实是说,不,我不希望使用LINQ到SQL供应商执行Where ,我想使用LINQ到对象实施Where

因此枚举

Table.Where(somePredicate)

导致在 SQL Server 上执行查询,同时枚举

Table.AsEnumerable().Where(somePredicate)

将 Table 表示的Table带入内存并在内存中执行Where功能(而不是在 SQL Server 上!)

这就是AsEnumerable :允许您隐藏IEnumerable方法的特定实现,而是使用标准实现。

除了可读性之外,我还想到了一个原因,尽管与查询实现有关:在通过另一个 Linq 提供程序返回的匿名类型上使用 Linq to Objects。 您不能转换为匿名类型(或匿名类型的集合),但您可以使用.AsEnumerable()为您执行转换。

示例:

// Get an IQueryable of anonymous types.
var query = from p in db.PeopleTable /* Assume Linq to SQL */
            select new { Name = p.Name, Age = p.Age };

// Execute the query and pull the results into an IEnumerable of anonymous types
var @enum = query.AsEnumerable();

// Use Linq to Objects methods to further refine.
var refined = from p in @enum
              select new
              {
                  Name = GetPrettyName(p.Name),
                  DOB = CalculateDOB(p.Age, DateTime.Now)
              };

很明显,这里的原因是我们想要使用类似 Linq to SQL 的东西将一些记录拉入匿名类型,然后在客户端上使用 Linq to Objects 执行一些自定义逻辑(通过 Linq to SQL 是不可能的) -边。

转换为IEnumerable<_anon>是不可能的,所以.AsEnumerable()是唯一的方法。

感谢所有回答帮助我拼凑的人。 =)

当我C# 6.0 in a Nutshell阅读C# 6.0 in a Nutshell书时。 下面是书中AsEnumerable一个例子。


目的是将IQueryable<T>序列转换为IEnumerable<T> ,强制后续查询运算符绑定到 Enumerable 运算符而不是 Queryable 运算符。 这会导致查询的其余部分在本地执行。

为了说明这一点,假设我们在 SQL Server 中有一个MedicalArticles表,并且想要使用 LINQ to SQL 或 EF 来检索摘要包含少于 100 个单词的所有关于流感的文章。 对于后一个谓词,我们需要一个正则表达式:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

var query = dataContext.MedicalArticles
            .Where (article => article.Topic == "influenza" &&
            wordCounter.Matches (article.Abstract).Count < 100);

问题在于 SQL Server 不支持正则表达式,因此 LINQ-to-db 提供程序会抛出异常,抱怨无法将查询转换为 SQL。 我们可以通过分两步查询来解决这个问题:首先通过 LINQ to SQL 查询检索所有关于流感的文章,然后在本地过滤少于 100 字的摘要:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
    .Where (article => article.Topic == "influenza");

IEnumerable<MedicalArticle> localQuery = sqlQuery
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

使用 AsEnumerable,我们可以在单个查询中执行相同的操作:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

调用 AsEnumerable 的另一种方法是调用 ToArray 或 ToList。 AsEnumerable 的优点是它不会强制立即执行查询,也不会创建任何存储结构。

这只是转换为 IEnumerable 的最好和最短的方法。 如果您在 Reflector 中查看它,您会发现它除了将对象作为 IEnumerable 返回之外什么都不做。

来自 MSDN:

除了将源的编译时类型从实现 IEnumerable(Of T) 的类型更改为 IEnumerable(Of T) 本身之外,AsEnumerable(Of TSource)(IEnumerable(Of TSource)) 方法没有任何作用。

匿名类型是提供这些扩展方法的主要原因。 (您不能在泛型参数中使用匿名类型)但是方法调用可以使用类型推断,允许您省略在泛型参数中指定类型。

如果对象上有一个与 Linq 扩展方法同名的方法,它会隐藏扩展方法。 使用 AsEnumerable 可以让您获得扩展。

这在 SP1 中似乎是新的。

昨天我有一行代码从数据表中提取成员标识符:-

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

在我安装 SP1 之前,它工作得很好。 现在它不会工作,除非它读取

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

编辑:我找到了真正的原因

这样您就可以在同一个 linq 语句中同时使用远程方法(例如 SQL 语句中的 WHERE)和本地方法。 如果不使用 AsEnumerable(即只是强制转换),它将使查询生成器尝试为包含本地方法的远程执行创建表达式树。 将 AsEnumerable 放入查询将导致该查询的其余部分在远程查询的结果上本地执行。

来自https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

表示数据库表的 Table 类型可以有一个 Where 方法,该方法将谓词参数作为表达式树并将该树转换为 SQL 以供远程执行。 如果不需要远程执行,例如因为谓词调用本地方法,则可以使用 AsEnumerable 方法来隐藏自定义方法,而是使标准查询运算符可用。

正如你所说,如果一个类型已经实现了IEnumerable<T>那么在转换到接口或调用AsEnumerable方法之间没有任何功能上的区别。

我的猜测(也只是猜测)是调用AsEnumerable提高了可读性并保留了其他 LINQ 扩展方法的流畅签名:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

它还允许未实现IEnumerable<T>例如DataTable )拥有自己的AsEnumerable扩展版本。 这允许您在针对这些类型的查询中继续使用相同的模式- 即使它是您正在调用的不同AsEnumerable方法 - 无需担心该类型是否真的实现了IEnumerable<T>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM