什么是在数组上使用AsEnumerable()?

时间:2015-12-10 13:55:05

标签: c# collections

我正在阅读Eric Lippert的a blog,他解释了为什么他几乎不会使用数组,以下部分让我很好奇:

  

如果您正在编写这样的API,请将数组包装在ReadOnlyCollection中并返回IEnumerable或IList或其他内容,但不返回数组。 (当然,不要简单地将数组转换为IEnumerable并认为你已经完成了!那仍然是传递变量;调用者可以简单地转换回数组!只传递一个数组,如果它被只读对象包裹起来。)

所以我对收藏品进行了一些讨论:

string[] array = new[] { "cat", "dog", "parrot" };
IEnumerable<string> test1 = array.AsEnumerable();
string[] secondArray = (string[])test1;

//array1[0] is now also "whale"
array[0] = "whale";

//11 interfaces
var iArray = array.GetType().GetInterfaces();
//Still 11 interfaces??
var iTest1 = test1.GetType().GetInterfaces();

我初始化一个数组,然后在其上使用AsEnumerable()方法将其转换为IEnumerable(或者我认为),但当我将其转换回新数组时,更改一个数组原始数组中的值,test1secondArray的值已更改为。显然,我刚刚对原始数组进行了2次新引用,而不是像IEnumerable那样创建一个新的ToArray()返回一个新数组。

当我比较数组和IEnumerable的接口时,它们都具有相同的接口。如果数组实际上没有做任何事情,为什么数组有这种方法呢?我知道AsEnumerable()在Linq-to-entities中有用它来获取IQueryable时可枚举的方法,但为什么要将这种方法添加到数组中呢?这种方法有实际用途吗?

编辑:Tim Schmelter的评论提出了一个非常好的观点,不应该被忽视:

&#34;它没那么无用。您可以在不破坏其余代码的情况下更改实际类型。所以你可以用数据库查询或列表或者hashset等替换数组,但AsEnumerable总是可以工作,其余代码也可以。所以AsEnumerable就像合同一样。&#34;

3 个答案:

答案 0 :(得分:11)

AsEnumerable只是将值转换为IEnumerable<T>的一种方法。它不会创建新对象。该方法的实现如下:

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}

根本没有创建新对象。

它有用的原因:

  1. 如果对象实现IEnumerable<T>但也有一个名为SelectWhere等的实例方法,则无法使用LINQ方法。 AsEnumerable允许您将其转换为IEnumerable以避免此类过载冲突。

  2. 如果您想将对象转换为IEnumerable<T>(无论出于何种原因),但T是匿名类型,则无法使用一个演员,你需要一个通用的方法来推断它的泛型参数。

答案 1 :(得分:6)

你在这里有几个好的答案;我以为我会添加一些支持点。

首先,仅供参考,a&#34;什么都不做&#34;像这样总是返回其参数的方法称为&#34; identity&#34;,原因很明显,输出与输入相同。身份似乎毫无用处,但实际上它们确实会不时派上用场。

其次,如果您确实希望将数组转换为与数组没有引用标识的只读序列,则可以进行标识投影:

<div class="view-wrapper">
   ... view content ...
</div>

或等效地:

var sequence = from item in array select item;

请注意,这里的投影是一个身份;我说他们很有用。

通常LINQ会优化身份投射;如果你说

var sequence = array.Select(item => item);

然后永远不会生成最终的身份投影,因为这只是浪费时间。也就是说,这意味着

from item in array where whatever select item;

array.Where(item => whatever)

但是,如果array.Where(item => whatever).Select(item => item) 是查询中的唯一事物,编译器会抑制身份投影,这正是为了让您可以制作投影无法转换回原始数组。

答案 2 :(得分:4)

AsEnumerable的目的很明确,如Servy's answer和此帖所述:Why use .AsEnumerable() rather than casting to IEnumerable<T>?

剩下的问题是:为什么IEnumerable<T>上会显示?这种情况就是这种情况,因为此处使用的扩展方法使用IEnumerable<T>作为其定义的类型。在ToString()上将其视为string。由于string是一个对象,因此它派生方法ToString(),但在string的情况下却无用。在这里,扩展方法导致该方法的“无用”可用性。