为什么使用.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; 

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

编辑 :作为一个推论,为什么有一个.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 DataContextTable的例子中是有用的。 由于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带入内存,并在内存中执行Wherefunction(而不是在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) );