C#范例:对列表的副作用

Pie*_*rre 12 c# linq paradigms list side-effects

我试图进一步了解副作用以及如何控制和应用它们.

在下面的航班列表中,我想设置满足条件的每个航班的属性:

IEnumerable<FlightResults> fResults = getResultsFromProvider();

//Set all non-stop flights description
fResults.Where(flight => flight.NonStop)
        .Select(flight => flight.Description = "Fly Direct!");
Run Code Online (Sandbox Code Playgroud)

在这个表达式中,我对我的列表有副作用.从我有限的知识,我知道前."LINQ仅用于查询 "和"列表中只有少数操作,分配或设置值不是其中之一"和"列表应该是不可变的".

  • 上面的LINQ语句有什么问题,应该如何更改?
  • 我在哪里可以获得有关上述场景的基本范例的更多信息?

Fem*_*ref 13

您有两种方法可以实现LINQ方式:

  1. 显式foreach循环

    foreach(Flight f in fResults.Where(flight => flight.NonStop))
      f.Description = "Fly Direct!";
    
    Run Code Online (Sandbox Code Playgroud)
  2. ForEach操作员,为副作用:

    fResults.Where(flight => flight.NonStop)
            .ForEach(flight => flight.Description = "Fly Direct!");
    
    Run Code Online (Sandbox Code Playgroud)

第一种方式对于这么简单的任务来说非常沉重,第二种方式只能用于非常短的物体.

现在,您可能会问自己为什么ForEachLINQ堆栈中没有运算符.这很简单 - LINQ应该是一种表达查询操作的功能方式,这尤其意味着没有任何操作符应该有副作用.设计团队决定不在ForEach堆栈中添加操作符,因为唯一的用法是它的副作用.

ForEach运营商的通常实现如下:

public static class EnumerableExtension
{
  public static void ForEach<T> (this IEnumerable<T> source, Action<T> action)
  {
    if(source == null)
      throw new ArgumentNullException("source");

    foreach(T obj in source)
      action(obj);

  }
}
Run Code Online (Sandbox Code Playgroud)


Guf*_*ffa 6

这种方法的一个问题是它根本不起作用.该查询是惰性的,这意味着它不会执行Select中的代码,直到您实际从查询中读取内容,而您永远不会这样做.

您可以通过.ToList()在查询结尾添加来解决此问题,但代码仍然使用副作用并丢弃实际结果.您应该使用结果来执行更新:

//Set all non-stop flights description
foreach (var flight in fResults.Where(flight => flight.NonStop)) {  
  flight.Description = "Fly Direct!";
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*Jon 5

您的LINQ代码不会"直接"违反您提到的指南,因为您没有修改列表本身; 你只是修改列表内容的一些属性.

但是,驱动这些指导原则的主要反对意见仍然存在:您不应该使用LINQ修改数据(同样,您也在滥用Select执行副作用).

不修改任何数据可以很容易地证明是合理的.请考虑以下代码段:

fResults.Where(flight => flight.NonStop)  
Run Code Online (Sandbox Code Playgroud)

你知道这在哪里修改航班属性吗?许多维护程序员也不会,因为他们会在以后的Where代码中停止阅读- 因为这是一个查询,所以后面的代码 显然没有副作用,对吗?

[Nitpick:当然,看到一个不保留返回值的查询是一个死的赠品,查询确实有副作用或代码应该被删除; 无论如何,"有些事情是错的".但是,当只有两行代码而不是页面上的页面时,更容易说出来.

作为一个正确的解决方案,我建议这样做:

foreach (var x in fResults.Where(flight => flight.NonStop))
{
    x.Description = "Fly Direct!";
}
Run Code Online (Sandbox Code Playgroud)

写入和读取都很容易.