【问题标题】:If IEnumerable data source is changed, it changes the results如果更改了 IEnumerable 数据源,它会更改结果
【发布时间】:2020-10-01 22:33:04
【问题描述】:

给定以下代码:

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 做得很好,但我希望它有时返回相同类型的源代码。

【问题讨论】:

  • 作为一个小演示,您可以绕过 IEnumerable 持有它自己的引用,如下所示:dotnetfiddle.net/VcI2ai。尽管请记住,在迭代期间它仍然具有引用。当然,如果有人为此调用 ToList() 之类的东西,然后继续使用 List,对源的任何更改都不会再反映在枚举中

标签: c# collections ienumerable consistency data-consistency


【解决方案1】:

是的,这是预期行为。

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

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

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

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

【讨论】:

    猜你喜欢
    • 2015-12-13
    • 2018-01-27
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多