1

给定以下代码:

using System.Linq;              
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        //Init data
        char[] chars = new char[10];
        FillData(chars);

        // Write the initial data
        PrintContents("Initial data:", chars);
        //Take some data:
        IEnumerable<char> acc = chars.Take(3);
        //View data
        PrintContents("Enum:", acc);

        //Edit data
        chars[0] = 'z';
        chars[1] = 'z';
        chars[2] = 'z';

        //View data again
        PrintContents("Enum after modifing source:", acc);

        //Restart data
        chars = new char[5];
        FillData(chars);

        //View data when source is replaced
        PrintContents("Enum after new source:", acc);
    }

    //Gets a ref
    private static void FillData(char[] data)
    {
        for(int i = 0; i < data.Length; i++)
        {
            data[i] = (char)('a' + i);
        }
    }

    private static void PrintContents(string what, IEnumerable<char> src)
    {
        System.Console.WriteLine(what);
        string s = "";
        foreach(char ch in src)
        {
            s += ch;
        }
        if(s.Length > 0)
        {
            System.Console.WriteLine(s);
        }
    }
}

我得到这个输出:

Initial data:
abcdefghij
Enum:
abc
Enum after modifing source:
zzz
Enum after new source:
zzz

我知道延迟执行,但这是预期的行为吗?这意味着我应该在不创建新集合的情况下重用 IEnumerable 或在 IEnumerable 上使用的任何数据,因为我可能会更改程序的结果。

这意味着 IEnumerable 也将持有对数据源的引用,即使它们也未被可见代码使用,并且在收集 IEnumerable 本身之前不会被垃圾收集。

我在最近的一个项目中经常使用 IEnumerable,我看到它们越多,我就越不喜欢它们。不要误会我的意思,Linq 做得很好,但我希望它有时返回相同类型的源。

4

1 回答 1

2

是的,这是预期的行为。

您应该将 LINQ 方法的结果视为“我枚举时的计算结果”,而不是“项目集合”。对我来说,更容易理解的是,当我第二次枚举它时,它会在我遍历项目时再次计算结果。

在源数据可能发生变化(如问题中的示例)或获取结果成本高昂(查询数据库是隐藏成本的常见情况)的情况下,这很重要。不幸的是,没有通用的方法来说明 enumerable 是昂贵的(即 DB)还是本质上是免费的(即 list),并且这两种情况 - 重复查询实时数据或重复枚举缓存结果 - 都是常用的。IQueryable在某种程度上表明代价高昂,懒惰地评估可枚举,但只是IEnumerable没有说明任何关于代价高昂/最新结果的信息。

您担心查询使数据源保持活动的时间可能比您预期的要长 - 是的,这是一个问题。您应该了解结果的预期用途,并考虑返回非惰性结果是否更好(即使用.ToList())。从一次性来源(数据库、文件和不可搜索的来源,如网络流)获取数据时要小心——通常更容易强制评估查询并返回List(或任何其他非惰性)集合以控制如何和处理数据源时。

例如,您应该强烈考虑将非延迟枚举传递给 ASP.Net MVC 视图 - 数据可能很容易迭代多次以呈现(甚至.Count()是一次迭代),因此在 DB 上延迟计算的可枚举可以轻松地将渲染页面的成本增加一倍或三倍。

于 2020-06-12T06:51:37.703 回答