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
是等效的。扩展方法有什么意义?
修改:作为推论,为什么在转换为.AsQueryable()
时有IQueryable<T>
方法是等效的?
答案 0 :(得分:81)
可读性是这里的主要问题。考虑一下
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 Table
中考虑DataContext
的示例时,有用的原因。由于Table
是IQueryable
,它会实现IEnumerable
。当您在Where
上调用Table
方法并枚举结果时,将执行代码,最终导致在SQL Server上执行SQL语句。 AsEnumerable
所做的是,不,我不想使用LINQ to SQL提供程序来执行Where
,我想使用Where
的LINQ to Objects实现。< / p>
因此枚举
Table.Where(somePredicate)
导致在SQL Server上执行查询,而在
上进行枚举Table.AsEnumerable().Where(somePredicate)
将Table
表示的表引入内存并在内存中执行Where
功能(而不是在SQL Server上执行!)
这是AsEnumerable
的要点:允许您隐藏IEnumerable
方法的特定实现,而是使用标准实现。
答案 1 :(得分:15)
除了可读性之外,我已经想到了一个原因,尽管与查询实现有关:使用Linq to Objects通过另一个Linq提供程序返回的匿名类型。您不能转换为匿名类型(或匿名类型的集合),但您可以使用.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()
是唯一可行的方法。
谢谢所有回答的人帮我拼凑这些东西。 =)
答案 2 :(得分:4)
我正在阅读这本书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的优势在于它不会强制立即执行查询,也不会创建任何存储结构。
答案 3 :(得分:3)
这是投射到IEnumerable的最好和最短的方式。如果你在Reflector中查看它,除了将对象作为IEnumerable返回外,你可以看到它什么也没做。
来自MSDN:
AsEnumerable(Of。) TSource)(IEnumerable(Of TSource)) 方法除了以外没有任何效果 更改源的编译时类型 来自实现的类型 IEnumerable(Of T)到IEnumerable(Of T) 本身。
答案 4 :(得分:3)
匿名类型是提供这些扩展方法的主要原因。 (你不能在泛型参数中使用匿名类型) 但是方法调用可以使用类型推断,允许您省略在泛型参数中指定类型。
答案 5 :(得分:3)
如果对象上的方法与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
表示数据库表的表类型可以具有Where方法,该方法将谓词参数作为表达式树并将树转换为SQL以进行远程执行。如果不需要远程执行,例如因为谓词调用本地方法,则AsEnumerable方法可用于隐藏自定义方法,而是使标准查询运算符可用。
答案 6 :(得分:2)
正如你所说,如果一个类型已经实现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>
- for example, DataTable
的类型拥有自己的AsEnumerable
扩展名版本。这允许您继续在针对这些类型的查询中使用相同模式 - 即使它是您正在调用的不同AsEnumerable
方法 - 而无需担心该类型是否真的实现IEnumerable<T>
。