[英]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;
在上面的例子中, stringsEnum1
和stringsEnum2
是等价的。 扩展方法的重点是什么?
编辑:作为推论,为什么在转换为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.