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

时间:2021-02-01 19:01:15

One of the extension methods on IEnumerable<T> is .AsEnumerable(). This method converts the enumerable object it was called on into an instance of IEnumerable<T>. However, since an object must implement IEnumerable<T> in order to apply to this extension method, converting to IEnumerable<T> is a simple matter of casting to IEnumerable<T>. My question is why does this method exist at all?

IEnumerable 上的一个扩展方法是.AsEnumerable()。此方法将其调用的可枚举对象转换为IEnumerable 的实例。但是,由于对象必须实现IEnumerable 才能应用于此扩展方法,因此转换为IEnumerable 是一个简单的问题,即转换为IEnumerable 。我的问题是为什么这种方法存在呢?

Example:

例:

List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;

In the example above, stringsEnum1 and stringsEnum2 are equivalent. What's the point of the extension method?

在上面的示例中,stringsEnum1和stringsEnum2是等效的。扩展方法有什么意义?

Edit: As a corollary, why is there an .AsQueryable() method when casting to IQueryable<T> is equivalent?

编辑:作为推论,为什么在转换为IQueryable 时有.AsQueryable()方法是等效的?

7 个解决方案

#1


75  

Readability is the main issue here. Consider that

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

Table.AsEnumerable().Where(somePredicate)

is far more readable than

比...更具可读性

((IEnumerable<TableObject>)Table).Where(somePredicate).

Or imagine wanting to execute part of the query on the SQL Server and the rest in memory:

或者想象一下,想要在SQL Server上执行部分查询,其余的在内存中执行:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

versus

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

Now, as for why such a method is useful at all think of the example of a Table in a LINQ to SQL DataContext. As Table is an IQueryable it implements IEnumerable. When you invoke a Where method on such a Table and enumerate through the results, code is executed that eventually causes a SQL statement to be executed on a SQL Server. What AsEnumerable does is says, no, I don't want to use the LINQ to SQL provider to execute the Where, I want to use the LINQ to Objects implementation of Where.

现在,至于为什么这样的方法在LINQ to SQL DataContext中考虑一个Table的例子是有用的。由于Table是IQueryable,它实现了IEnumerable。当您在此类表上调用Where方法并枚举结果时,将执行代码,最终导致在SQL Server上执行SQL语句。 AsEnumerable的作用是什么,不,我不想使用LINQ to SQL提供程序来执行Where,我想使用LINQ to Objects实现Where。

Thus enumerating over

因此列举了

Table.Where(somePredicate)

causes a query to be executed on a SQL Server whereas enumerating over

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

Table.AsEnumerable().Where(somePredicate)

brings the table represented by Table into memory and executes the Where functionality in memory (and not on the SQL Server!)

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

This is the point of AsEnumerable: to allow you to hide a specific implementation of IEnumerable methods and instead use the standard implementation.

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

#2


14  

I've thought of a reason apart from readability, though related to query implementation: using Linq to Objects on anonymous types returned via another Linq provider. You can't cast to an anonymous type (or a collection of anonymous types), but you can use .AsEnumerable() to perform the cast for you.

我想到了除可读性之外的原因,尽管与查询实现有关:使用Linq to Objects通过另一个Linq提供程序返回的匿名类型。您不能转换为匿名类型(或匿名类型的集合),但您可以使用.AsEnumerable()为您执行转换。

Example:

例:

// 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)
              };

Clearly the reason here is that we want to use something like Linq to SQL to pull down some records into an anonymous type, then perform some custom logic (that wouldn't be possible via Linq to SQL) using Linq to Objects on the client-side.

显然,这里的原因是我们希望使用Linq to SQL之类的东西将一些记录下拉成匿名类型,然后使用Linq to Client在客户端上执行一些自定义逻辑(通过Linq to SQL无法实现) - 侧。

Casting to IEnumerable<_anon> isn't possible, so .AsEnumerable() is the only way to go.

无法转换为IEnumerable <_anon>,因此.AsEnumerable()是唯一的方法。

Thanks everyone who answered to help me piece this together. =)

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

#3


4  

As I'm reading the book C# 6.0 in a Nutshell. Below is an example of AsEnumerable in the book.

正如我在Nutshell中读到C#6.0这本书。下面是本书中AsEnumerable的一个例子。


The purpose is to cast an IQueryable<T> sequence to IEnumerable<T>, forcing subsequent query operators to bind to Enumerable operators instead of Queryable operators. This causes the remainder of the query to execute locally.

目的是将一个IQueryable 序列转换为IEnumerable ,强制后续查询运算符绑定到Enumerable运算符而不是Queryable运算符。这会导致查询的其余部分在本地执行。

To illustrate, suppose we had a MedicalArticles table in SQL Server and wanted to use LINQ to SQL or EF to retrieve all articles on influenza whose abstract contained less than 100 words. For the latter predicate, we need a regular expression:

为了说明,假设我们在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);

The problem is that SQL Server doesn’t support regular expressions, so the LINQ-to-db providers will throw an exception, complaining that the query cannot be translated to SQL. We can solve this by querying in two steps: first retrieving all articles on influenza through a LINQ to SQL query, and then filtering locally for abstracts of less than 100 words:

问题是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);

With AsEnumerable, we can do the same in a single query:

使用AsEnumerable,我们可以在一个查询中执行相同的操作:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

An alternative to calling AsEnumerable is to call ToArray or ToList. The advantage of AsEnumerable is that it doesn’t force immediate query execution, nor does it create any storage structure.

调用AsEnumerable的另一种方法是调用ToArray或ToList。 AsEnumerable的优点是它不会强制立即执行查询,也不会创建任何存储结构。

#4


3  

It's just a nicest and shortest way to cast to an IEnumerable. If you look at it in Reflector, you can see it does nothing except return the object as an IEnumerable.

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

From MSDN:

来自MSDN:

The AsEnumerable(Of TSource)(IEnumerable(Of TSource)) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable(Of T) to IEnumerable(Of T) itself.

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

#5


3  

Anonymous types are a main reason to provide these kinds of extension methods. (you cannot use anonymous types in generics parameters) But a method call can use type inference allowing you to omit specifying the type in the generic parameters.

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

#6


3  

If there is a method on an object that has the same name as a Linq extension method it hides the extension method. Using AsEnumerable allows you to get at the extension.

如果对象上有一个与Linq扩展方法同名的方法,则会隐藏扩展方法。使用AsEnumerable可以获得扩展名。

This appears to be new in SP1.

这在SP1中似乎是新的。

Yesterday I had a line of code that extracted member identifiers from a data table:-

昨天我有一行代码从数据表中提取成员标识符: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

which worked just fine until I installed SP1. Now it won't work unless it reads

哪个工作正常,直到我安装SP1。现在它除非它读取它将无法工作

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

Edit: I found the real reason

编辑:我找到了真正的原因

It's so that you can use both remote methods (e.g. WHERE in a SQL statement) and local methods in the same linq statement. Without using AsEnumerable (i.e. just casting) it will make the query generator attempt to create an expression tree for remote execution that contains the local method. Putting AsEnumerable into the query will cause the rest of that query to be executed locally on the results of the remote query.

这样您就可以在同一个linq语句中使用远程方法(例如,SQL语句中的WHERE)和本地方法。如果不使用AsEnumerable(即只是转换),它将使查询生成器尝试为包含本地方法的远程执行创建表达式树。将AsEnumerable放入查询将导致该查询的其余部分在远程查询的结果上本地执行。

From https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

来自https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

A Table type that represents a database table could have a Where method that takes the predicate argument as an expression tree and converts the tree to SQL for remote execution. If remote execution is not desired, for example because the predicate invokes a local method, the AsEnumerable method can be used to hide the custom methods and instead make the standard query operators available.

表示数据库表的表类型可以具有Where方法,该方法将谓词参数作为表达式树并将树转换为SQL以进行远程执行。如果不需要远程执行,例如因为谓词调用本地方法,则AsEnumerable方法可用于隐藏自定义方法,而是使标准查询运算符可用。

#7


2  

As you say, if a type already implements IEnumerable<T> then there's not really any functional difference between casting to the interface or calling the AsEnumerable method.

如你所说,如果一个类型已经实现了IEnumerable ,那么在转换到接口或调用AsEnumerable方法之间实际上没有任何功能差异。

My guess, and it's only a guess, is that calling AsEnumerable improves readability and retains the fluent signature of other LINQ extension methods:

我猜,这只是猜测,调用AsEnumerable提高了可读性并保留了其他LINQ扩展方法的流畅签名:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

It also allows types that don't implement IEnumerable<T> - for example, DataTable - to have their own version of the AsEnumerable extension. This allows you to continue using the same pattern in queries against those types - even though it's a different AsEnumerable method that you're calling - without needing to worry about whether or not the type really implements IEnumerable<T>.

它还允许不实现IEnumerable 的类型(例如,DataTable)拥有自己的AsEnumerable扩展版本。这允许您在针对这些类型的查询中继续使用相同的模式 - 即使它是您正在调用的不同的AsEnumerable方法 - 而无需担心该类型是否真正实现了IEnumerable

#1


75  

Readability is the main issue here. Consider that

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

Table.AsEnumerable().Where(somePredicate)

is far more readable than

比...更具可读性

((IEnumerable<TableObject>)Table).Where(somePredicate).

Or imagine wanting to execute part of the query on the SQL Server and the rest in memory:

或者想象一下,想要在SQL Server上执行部分查询,其余的在内存中执行:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

versus

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

Now, as for why such a method is useful at all think of the example of a Table in a LINQ to SQL DataContext. As Table is an IQueryable it implements IEnumerable. When you invoke a Where method on such a Table and enumerate through the results, code is executed that eventually causes a SQL statement to be executed on a SQL Server. What AsEnumerable does is says, no, I don't want to use the LINQ to SQL provider to execute the Where, I want to use the LINQ to Objects implementation of Where.

现在,至于为什么这样的方法在LINQ to SQL DataContext中考虑一个Table的例子是有用的。由于Table是IQueryable,它实现了IEnumerable。当您在此类表上调用Where方法并枚举结果时,将执行代码,最终导致在SQL Server上执行SQL语句。 AsEnumerable的作用是什么,不,我不想使用LINQ to SQL提供程序来执行Where,我想使用LINQ to Objects实现Where。

Thus enumerating over

因此列举了

Table.Where(somePredicate)

causes a query to be executed on a SQL Server whereas enumerating over

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

Table.AsEnumerable().Where(somePredicate)

brings the table represented by Table into memory and executes the Where functionality in memory (and not on the SQL Server!)

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

This is the point of AsEnumerable: to allow you to hide a specific implementation of IEnumerable methods and instead use the standard implementation.

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

#2


14  

I've thought of a reason apart from readability, though related to query implementation: using Linq to Objects on anonymous types returned via another Linq provider. You can't cast to an anonymous type (or a collection of anonymous types), but you can use .AsEnumerable() to perform the cast for you.

我想到了除可读性之外的原因,尽管与查询实现有关:使用Linq to Objects通过另一个Linq提供程序返回的匿名类型。您不能转换为匿名类型(或匿名类型的集合),但您可以使用.AsEnumerable()为您执行转换。

Example:

例:

// 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)
              };

Clearly the reason here is that we want to use something like Linq to SQL to pull down some records into an anonymous type, then perform some custom logic (that wouldn't be possible via Linq to SQL) using Linq to Objects on the client-side.

显然,这里的原因是我们希望使用Linq to SQL之类的东西将一些记录下拉成匿名类型,然后使用Linq to Client在客户端上执行一些自定义逻辑(通过Linq to SQL无法实现) - 侧。

Casting to IEnumerable<_anon> isn't possible, so .AsEnumerable() is the only way to go.

无法转换为IEnumerable <_anon>,因此.AsEnumerable()是唯一的方法。

Thanks everyone who answered to help me piece this together. =)

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

#3


4  

As I'm reading the book C# 6.0 in a Nutshell. Below is an example of AsEnumerable in the book.

正如我在Nutshell中读到C#6.0这本书。下面是本书中AsEnumerable的一个例子。


The purpose is to cast an IQueryable<T> sequence to IEnumerable<T>, forcing subsequent query operators to bind to Enumerable operators instead of Queryable operators. This causes the remainder of the query to execute locally.

目的是将一个IQueryable 序列转换为IEnumerable ,强制后续查询运算符绑定到Enumerable运算符而不是Queryable运算符。这会导致查询的其余部分在本地执行。

To illustrate, suppose we had a MedicalArticles table in SQL Server and wanted to use LINQ to SQL or EF to retrieve all articles on influenza whose abstract contained less than 100 words. For the latter predicate, we need a regular expression:

为了说明,假设我们在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);

The problem is that SQL Server doesn’t support regular expressions, so the LINQ-to-db providers will throw an exception, complaining that the query cannot be translated to SQL. We can solve this by querying in two steps: first retrieving all articles on influenza through a LINQ to SQL query, and then filtering locally for abstracts of less than 100 words:

问题是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);

With AsEnumerable, we can do the same in a single query:

使用AsEnumerable,我们可以在一个查询中执行相同的操作:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

An alternative to calling AsEnumerable is to call ToArray or ToList. The advantage of AsEnumerable is that it doesn’t force immediate query execution, nor does it create any storage structure.

调用AsEnumerable的另一种方法是调用ToArray或ToList。 AsEnumerable的优点是它不会强制立即执行查询,也不会创建任何存储结构。

#4


3  

It's just a nicest and shortest way to cast to an IEnumerable. If you look at it in Reflector, you can see it does nothing except return the object as an IEnumerable.

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

From MSDN:

来自MSDN:

The AsEnumerable(Of TSource)(IEnumerable(Of TSource)) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable(Of T) to IEnumerable(Of T) itself.

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

#5


3  

Anonymous types are a main reason to provide these kinds of extension methods. (you cannot use anonymous types in generics parameters) But a method call can use type inference allowing you to omit specifying the type in the generic parameters.

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

#6


3  

If there is a method on an object that has the same name as a Linq extension method it hides the extension method. Using AsEnumerable allows you to get at the extension.

如果对象上有一个与Linq扩展方法同名的方法,则会隐藏扩展方法。使用AsEnumerable可以获得扩展名。

This appears to be new in SP1.

这在SP1中似乎是新的。

Yesterday I had a line of code that extracted member identifiers from a data table:-

昨天我有一行代码从数据表中提取成员标识符: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

which worked just fine until I installed SP1. Now it won't work unless it reads

哪个工作正常,直到我安装SP1。现在它除非它读取它将无法工作

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

Edit: I found the real reason

编辑:我找到了真正的原因

It's so that you can use both remote methods (e.g. WHERE in a SQL statement) and local methods in the same linq statement. Without using AsEnumerable (i.e. just casting) it will make the query generator attempt to create an expression tree for remote execution that contains the local method. Putting AsEnumerable into the query will cause the rest of that query to be executed locally on the results of the remote query.

这样您就可以在同一个linq语句中使用远程方法(例如,SQL语句中的WHERE)和本地方法。如果不使用AsEnumerable(即只是转换),它将使查询生成器尝试为包含本地方法的远程执行创建表达式树。将AsEnumerable放入查询将导致该查询的其余部分在远程查询的结果上本地执行。

From https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

来自https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

A Table type that represents a database table could have a Where method that takes the predicate argument as an expression tree and converts the tree to SQL for remote execution. If remote execution is not desired, for example because the predicate invokes a local method, the AsEnumerable method can be used to hide the custom methods and instead make the standard query operators available.

表示数据库表的表类型可以具有Where方法,该方法将谓词参数作为表达式树并将树转换为SQL以进行远程执行。如果不需要远程执行,例如因为谓词调用本地方法,则AsEnumerable方法可用于隐藏自定义方法,而是使标准查询运算符可用。

#7


2  

As you say, if a type already implements IEnumerable<T> then there's not really any functional difference between casting to the interface or calling the AsEnumerable method.

如你所说,如果一个类型已经实现了IEnumerable ,那么在转换到接口或调用AsEnumerable方法之间实际上没有任何功能差异。

My guess, and it's only a guess, is that calling AsEnumerable improves readability and retains the fluent signature of other LINQ extension methods:

我猜,这只是猜测,调用AsEnumerable提高了可读性并保留了其他LINQ扩展方法的流畅签名:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

It also allows types that don't implement IEnumerable<T> - for example, DataTable - to have their own version of the AsEnumerable extension. This allows you to continue using the same pattern in queries against those types - even though it's a different AsEnumerable method that you're calling - without needing to worry about whether or not the type really implements IEnumerable<T>.

它还允许不实现IEnumerable 的类型(例如,DataTable)拥有自己的AsEnumerable扩展版本。这允许您在针对这些类型的查询中继续使用相同的模式 - 即使它是您正在调用的不同的AsEnumerable方法 - 而无需担心该类型是否真正实现了IEnumerable