【问题标题】:What's the use of AsEnumerable() on an array?数组上的 AsEnumerable() 有什么用?
【发布时间】:2015-12-10 13:55:04
【问题描述】:

我正在阅读 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 的评论提出了一个非常好的观点,不应忽视:

“它不是那么没用。您可以更改实际类型而不会破坏其余代码。因此您可以将数组替换为数据库查询或列表或哈希集或其他任何东西,但 AsEnumerable 始终有效并且其余的代码也在后面。所以 AsEnumerable 就像一个合同。”

【问题讨论】:

  • 也许这可以帮助你:stackoverflow.com/questions/2013846/…
  • The AsEnumerable&lt;TSource&gt;(IEnumerable&lt;TSource&gt;) method has no effect other than to change the compile-time type of source from a type that implements IEnumerable&lt;T&gt; to IEnumerable&lt;T&gt; itself. msdn.microsoft.com/library/bb335435(v=vs.100).aspx
  • 上面的含义(我引用的)意味着您可以以不同的方式迭代它。不确定它还提供什么。
  • AsEnumerable() 不是数组的方法。这是IEnumerable&lt;T&gt; 的扩展方法。因为数组恰好实现了IEnumerable&lt;T&gt;,所以你可以在数组上调用它。
  • @AlexanderDerck 该方法在那里,因为它是 IEnumerable 上的扩展方法,并且数组实现了 IEnumerable。

标签: c# collections


【解决方案1】:

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

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

它根本没有创建一个新对象。

有用的原因:

  1. 如果一个对象实现了IEnumerable&lt;T&gt;,但也有一个名为SelectWhere等的实例方法,则不能使用LINQ方法。 AsEnumerable 允许您将其强制转换为 IEnumerable 以避免此类重载冲突。

  2. 如果您想将对象强制转换为 IEnumerable&lt;T&gt;(无论出于何种原因),但 T 是匿名类型,您不能使用强制转换,您需要一个可以推断其通用参数的通用方法。

【讨论】:

    【解决方案2】:

    您在这里有几个很好的答案;我想我会添加一些支持点。

    首先,仅供参考,像这样总是返回其参数的“什么都不做”方法称为“身份”,原因很明显,输出与输入相同。身份看似无用,但实际上有时确实会派上用场。

    其次,如果你确实想将一个数组变成一个只读序列,并且对数组没有引用标识,你可以做一个标识投影:

    var sequence = from item in array select item;
    

    或等效:

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

    注意这里的投影是一个恒等式;我说它们很有用。

    通常 LINQ 会优化掉一个身份投影;如果你说

    from item in array where whatever select item;
    

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

    array.Where(item => whatever)
    

    不是

    array.Where(item => whatever).Select(item => item)
    

    但编译器确实select是查询中的唯一事物的情况下抑制身份投影,正是为了让您可以进行投影无法转换回原始数组。

    【讨论】:

    • 所以要真正获得IEnumerable 而不是对原始数组的引用是array.Select(x =&gt; x).AsEnumerable() 吗?
    • @AlexanderDerck AsEnumerable 没必要,投影就够了。
    【解决方案3】:

    AsEnumerable 的用途非常明确,如 Servy's answer 和此帖子中所述,例如:Why use .AsEnumerable() rather than casting to IEnumerable<T>?

    剩下的问题是:为什么它在IEnumerable&lt;T&gt; 上可见?这很简单,因为这里使用的扩展方法使用IEnumerable&lt;T&gt; 作为它定义的类型。在string 上将其视为ToString()。由于string 是一个对象,它派生了ToString() 方法,尽管在string 的情况下无用。在这里,扩展方法导致该方法的这种“无用”可用性。

    【讨论】:

    • 没那么没用。您可以在不破坏其余代码的情况下更改实际类型。因此,您可以用数据库查询或列表或哈希集或其他任何东西替换数组,但 AsEnumerable 始终有效,其余代码也在之后。所以AsEnumerable 就像一个合同。
    • 这不是在回答所提出的问题,它只是提供切向相关的信息,因此,应该只是一个评论。
    • 确实,Servy 很好地涵盖了这一点。我只是想指出“如果数组实际上根本没有做任何事情,为什么它有那个方法?”问题的一部分。它在某些情况下确实有目的,但不是全部。
    • 是的,我注意到将其转换为IEnumerableAsEnumerable() 做的事情相同,而我正在搞砸。不过,所有这些扩展方法都会让人感到困惑,有时 c# 对我来说有点太抽象了:p
    • @PatrickHofman 你认为为什么会这样?问题是询问这种方法的目的是什么,以及为什么它会有用。你只是说我回答了这个问题。那不是在回答问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多