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>
是相同的?
可读性是这里的主要问题。考虑一下
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实现。
因此列举了
Table.Where(somePredicate)
导致查询在SQL Server上执行而枚举结束
Table.AsEnumerable().Where(somePredicate)
将Table
表示的表带入内存并在内存中执行Where
功能(而不是在SQL Server上执行!)
这是AsEnumerable
的要点:允许您隐藏IEnumerable
方法的特定实现,而是使用标准实现。
我想到了除了可读性之外的一个原因,尽管与查询实现有关:使用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客户端上的对象执行一些自定义逻辑(通过Linq to SQL无法实现) - 侧。
铸造到IEnumerable<_anon>
是不可能的,所以.AsEnumerable()
是唯一的出路。
谢谢所有回答的人帮我拼凑这个。 =)
因为我正在读这本书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:
AsEnumerable(Of TSource)(IEnumerable(Of TSource))方法除了将源的编译时类型从实现IEnumerable(Of T)的类型更改为IEnumerable(Of T)本身之外没有任何效果。
匿名类型是提供这些扩展方法的主要原因。 (您不能在泛型参数中使用匿名类型)但是方法调用可以使用类型推断,允许您省略在泛型参数中指定类型。
如果对象上有一个与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方法可用于隐藏自定义方法,而是使标准查询运算符可用。
如你所说,如果一个类型已经实现了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>
。