为什么使用.AsEnumerable()而不是强制转换为IEnumerable ?

问题描述 投票:62回答:7

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>是相同的?

c# linq ienumerable
7个回答
80
投票

可读性是这里的主要问题。考虑一下

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实现。

因此列举了

Table.Where(somePredicate)

导致查询在SQL Server上执行而枚举结束

Table.AsEnumerable().Where(somePredicate)

Table表示的表带入内存并在内存中执行Where功能(而不是在SQL Server上执行!)

这是AsEnumerable的要点:允许您隐藏IEnumerable方法的特定实现,而是使用标准实现。


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客户端上的对象执行一些自定义逻辑(通过Linq to SQL无法实现) - 侧。

铸造到IEnumerable<_anon>是不可能的,所以.AsEnumerable()是唯一的出路。

谢谢所有回答的人帮我拼凑这个。 =)


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
投票

这是投射到IEnumerable的最好和最短的方式。如果你在Reflector中查看它,除了将对象作为IEnumerable返回之外,你可以看到它什么都不做。

来自MSDN:

AsEnumerable(Of TSource)(IEnumerable(Of TSource))方法除了将源的编译时类型从实现IEnumerable(Of T)的类型更改为IEnumerable(Of T)本身之外没有任何效果。


3
投票

匿名类型是提供这些扩展方法的主要原因。 (您不能在泛型参数中使用匿名类型)但是方法调用可以使用类型推断,允许您省略在泛型参数中指定类型。


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方法可用于隐藏自定义方法,而是使标准查询运算符可用。


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>

© www.soinside.com 2019 - 2024. All rights reserved.