Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 故意依赖Linq的副作用是一种不好的做法吗?_C#_Linq_Side Effects - Fatal编程技术网

C# 故意依赖Linq的副作用是一种不好的做法吗?

C# 故意依赖Linq的副作用是一种不好的做法吗?,c#,linq,side-effects,C#,Linq,Side Effects,这样的编程模式经常出现: int staleCount = 0; fileUpdatesGridView.DataSource = MultiMerger.TargetIds .Select(id => { FileDatabaseMerger merger = MultiMerger.GetMerger(id); if (merger.TargetIsStale) staleCount++; r

这样的编程模式经常出现:

int staleCount = 0;

fileUpdatesGridView.DataSource = MultiMerger.TargetIds
    .Select(id =>
    {
        FileDatabaseMerger merger = MultiMerger.GetMerger(id);

        if (merger.TargetIsStale)
            staleCount++;

        return new
        {
            Id = id,
            IsStale = merger.TargetIsStale,
            // ...
        };
    })
    .ToList();

fileUpdatesGridView.DataBind();
fileUpdatesMergeButton.Enabled = staleCount > 0;
我不确定有没有更简洁的方法来编写代码


即使是这样,这样做是不是不好的做法?

为什么不这样编码呢:

var result=MultiMerger.TargetIds
    .Select(id =>
    {
        FileDatabaseMerger merger = MultiMerger.GetMerger(id);

        return new
        {
            Id = id,
            IsStale = merger.TargetIsStale,
            // ...
        };
    })
    .ToList();
fileUpdatesGridView.DataSource = result;
fileUpdatesGridView.DataBind();
fileUpdatesMergeButton.Enabled = result.Any(r=>r.IsStale);
我认为这是不好的做法。您假设lambda表达式由于调用了ToList而被强制执行。这是ToList当前版本的实现细节。如果.NET7.x中的ToList被更改为返回一个半惰性地转换IQueryable的对象,该怎么办?如果它被更改为并行运行lambda呢?突然之间,您的staleCount上出现了并发问题。据我所知,这两种可能性都会破坏您的代码,因为您的代码做出了错误的假设

现在,只要使用单个id重复调用multimerge.getmerge,就应该将其修改为一个连接,因为执行连接的逻辑(w | c)将比您在那里编写的代码效率更高,并且可以更好地扩展,特别是如果multimerge的实现实际上是从数据库中提取数据的话(或可能更改为这样做)

至于在将ToList()传递给数据源之前调用它,如果数据源没有使用新对象中的所有字段,那么您可能会跳过ToList的速度更快,占用的内存更少,让数据源只提取它所需的字段。您所做的是将数据与视图的确切要求高度耦合,这应该尽可能避免。例如,如果突然需要显示FileDatabaseMerge中存在但不在您中的字段,该怎么办r当前匿名对象?现在您必须更改控制器和视图才能添加它,如果您刚刚传入IQueryable,您只需更改视图。同样,更快、更少的内存、更灵活和更易于维护

希望这有帮助。这个问题真的应该发布在代码审查上,而不是stackoverflow上

进一步查看更新后,以下代码会更好一些

var result=MultiMerger.GetMergersByIds(MultiMerger.TargetIds);
fileUpdatesGridView.DataSource = result;
fileUpdatesGridView.DataBind();
fileUpdatesMergeButton.Enabled = result.Any(r=>r.TargetIsStale);

不,这并不是严格意义上的“坏习惯”(比如用用户输入的字符串串接构造SQL查询或使用
goto

有时,这样的代码比几个查询/
foreach
更可读,或者没有副作用
Aggregate
call。此外,最好至少尝试编写
foreach
且没有副作用的版本,看看哪一个更可读/更容易证明正确性

请注意:

  • 通常很难推断此类代码将发生什么/何时发生。例如,您可以使用
    .ToList()
    调用来模拟延迟执行LINQ查询的情况,否则将不会计算该值
  • 纯功能可以并行运行,一旦出现副作用,就需要非常小心
  • 如果您需要将LINQ转换为对象,将LINQ转换为SQL,则必须重写此类查询
  • 通常LINQ查询支持没有副作用的函数式编程风格(因此按照惯例,读者不会期望代码中有副作用)

您所说的副作用是什么?您正在使用枚举,确保它已执行且不会再次执行-因此,只要在执行之前重置
staleCount,它就没有问题。副作用是修改“在linq管道之外”的staleCount,并在Select()中执行而不是Foreach()。这是示例代码/存根代码/假设代码,如果发布到,则将按此关闭。需要具体的工作代码。我建议您在提出更多建议之前仔细阅读@SamAxe。我投票将此问题作为非主题关闭,因为它不是一个特定的编程问题,而是一个轮询。Any()包括未过期的记录,因此无法使用。不过,看起来您应该使用左连接而不是函数。我喜欢它。那么您是说这是一种不好的做法吗?是的,这是一种不好的做法。如果您通过一个好的代码分析器运行代码,它可能也会标记它,原因有很多……select是run laz但是,您可以通过使用
ToList
强制它运行。在将它传递到数据源之前,我也不会调用
ToList
。让它获取查询,以防它想执行排序和分页等操作。您已经克服了这种惰性,您已经手动实现了左连接(可能效率不高),然后您就有了在惰性函数中计算并在查询范围外使用的变量。是的,知道延迟的exec,但还有其他原因。没有ToList()您的示例不会中断吗因为它需要执行两次?我认为这是一种糟糕的做法,因为LINQ实现不能保证枚举行为在将来不会被更改或优化。虽然.ToList()可能会继续强制枚举所有元素,.NET Core已经在优化.Count()和.Skip()。这将减少从原始集合中迭代的元素数量,如果依赖于副作用,则会引入突破性的更改。
var result=MultiMerger.GetMergers().Where(m=>MultiMerger.TargetIds.Contains(m.Id));
fileUpdatesGridView.DataSource = result;
fileUpdatesGridView.DataBind();
fileUpdatesMergeButton.Enabled = result.Any(r=>r.TargetIsStale);