为什么使用.AsEnumerable()而不是转换为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
是等价的。 扩展方法的要点是什么?
编辑 :作为一个推论,为什么有一个.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 DataContext
的Table
的例子中是有用的。 由于Table
是一个IQueryable
它实现了IEnumerable
。 当您在这样的Table
上调用Where
方法并枚举结果时,将执行代码,最终导致SQL语句在SQL Server上执行。 AsEnumerable
所做的就是说,不,我不想使用LINQ to SQL提供程序来执行Where
,我想使用LINQ to Objects实现Where
。
如此列举
Table.Where(somePredicate)
导致查询在SQL Server上执行,而列举
Table.AsEnumerable().Where(somePredicate)
将Table表示的Table
带入内存,并在内存中执行Where
function(而不是在SQL Server上)
这是AsEnumerable
:允许您隐藏IEnumerable
方法的特定实现,而是使用标准实现。
我想到了一个除了可读性之外的原因,尽pipe与查询实现有关:在通过另一个Linq提供程序返回的匿名types上使用Linq to Objects。 您不能转换为匿名types(或匿名types的集合),但可以使用.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之类的东西将一些logging下拉成匿名types,然后使用Linq to客户机上的对象来执行一些自定义逻辑(这不可能通过Linq to SQL)侧。
铸造到IEnumerable<_anon>
是不可能的,所以.AsEnumerable()
是唯一的方法。
谢谢大家回答,帮我把这件作品拼在一起。 =)
这只是一个最好的和最简单的方式投给IEnumerable。 如果您在Reflector中查看它,可以看到它除了以IEnumerable的forms返回对象之外什么也不做。
来自MSDN:
除了将源的编译时types从实现IEnumerable(Of T)的types更改为IEnumerable(Of T)本身之外,AsEnumerable(Of TSource)(IEnumerable(Of TSource))方法没有效果。
正如你所说,如果一个types已经实现IEnumerable<T>
那么在转换到接口或调用AsEnumerable
方法之间并没有真正的function差异。
我的猜测是,只是猜测,调用AsEnumerable
提高了可读性,并保留了其他LINQ扩展方法的stream畅签名:
var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty); // vs var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);
它还允许不实现IEnumerable<T>
( 例如DataTable
)拥有自己的AsEnumerable
扩展版本。 这允许您继续在这些types的查询中使用相同的模式 – 即使它是您调用的另一个AsEnumerable
方法 – 而不必担心该types是否真正实现了IEnumerable<T>
。
匿名types是提供这种扩展方法的主要原因。 (不能在generics参数中使用匿名types)但是方法调用可以使用types推断,从而可以省略在generics参数中指定types。
正如我在简单阅读C# 6.0 in a Nutshell
的书。 下面是本书中的一个例子。
目的是将一个IQueryable<T>
序列强制转换为IEnumerable<T>
,强制后续的查询操作符绑定到Enumerable操作符而不是Queryable操作符。 这会导致查询的其余部分在本地执行。
为了说明这一点,假设我们在SQL Server中有一个MedicalArticles
表,并希望使用LINQ to SQL或EF来检索所有stream感的摘要包含less于100个单词的文章。 对于后一个谓词,我们需要一个正则expression式:
Regex wordCounter = new Regex (@"\b(\w|[-'])+\b"); var query = dataContext.MedicalArticles .Where (article => article.Topic == "influenza" && wordCounter.Matches (article.Abstract).Count < 100);
问题是SQL Server不支持正则expression式,所以LINQ-to-db提供程序会抛出exception,抱怨查询不能转换为SQL。 我们可以通过两步查询来解决这个问题:首先通过LINQ to SQL查询检索stream感的所有文章,然后在本地过滤less于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的优点是不会强制立即执行查询 ,也不会创build任何存储结构。
如果在与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) );