为什么使用.AsEnumerable()而不是转换为IEnumerable <t>?</t>

时间:2010-01-06 15:10:57

标签: c# linq ienumerable

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>方法是等效的?

7 个答案:

答案 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的示例时,有用的原因。由于TableIQueryable,它会实现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>