访问修改后的Closure

时间:2011-07-06 00:13:14

标签: c# .net lambda

这会失败吗?     Resharper将此报告为“访问修改后的关闭”的实例     是否会为每个值触发lambda?在更改this的行运行之前,迭代器是否在first中生成所有间隔值的完整列表?或者是为了达到迭代而运行的行first = itvl; ,以及用于后续迭代的first的更改值?

 public HourInterval FirstInterval
    {
        get
        {
            var first = HourInterval.Make(DateTime.MaxValue);
            foreach (var itvl in this.Where
                        (itvl => itvl < first))
                first = itvl;
            return first;
        }
    }

请注意。 HourInterval是一个值类型结构,代表每个一小时的日历时间......而thisIEnumerableHourInterval个对象的集合

编辑:

以上是Resharper建议从以下foreach构造中转换为LINQ表达式的内容......

    public HourInterval FirstInterval
    {
        get
        {
            var first = HourInterval.Make(DateTime.MaxValue);
            foreach (var itvl in this)
               if(itvl < first)
                  first = itvl;
            return first;
        }
    }

2 个答案:

答案 0 :(得分:14)

好的,这有点乱。

首先,在同一代码中以两种稍微不一致的方式使用相同的变量名是一种糟糕的编程习惯。这让人非常困惑。坦率地说,我宁愿这是非法的;它不违法的原因有点复杂;有关详细信息,请参阅http://blogs.msdn.com/b/ericlippert/archive/2009/11/05/simple-names-are-not-so-simple-part-two.aspx

让我们摆脱这个问题:

var first = HourInterval.Make(DateTime.MaxValue);
foreach (var itvl in this.Where(x => x < first))
  first = itvl;

现在,接下来的问题是:Resharper是否正确注意到这是对已修改闭包的访问?是的,Resharper是正确的;您正在修改将重复调用的lambda的已关闭变量。 Resharper注意到这是危险的,因为Resharper不知道什么&#34;哪里&#34;确实。所有Resharper都知道,&#34; Where&#34;正在缓存它获得的每个谓词并将其保存以便稍后执行,错误地认为每个谓词都会做一些不同的事情。事实上,每个谓词都是相同的,因为每个谓词都在同一个变量上关闭,而不是在不同的变量上关闭。

显然没有合理的实施&#34; Where&#34;会这样做的。但是Resharper并不知道。

接下来的问题是:这是一件明智的事吗?不。这是通过将谓词的封闭变量修改为&#34;其中&#34;来实现&#34; Min&#34;的非常简单和混乱的方式。如果你想写Min,只需写Min:

static DateTime? Min(this IEnumerable<DateTime> seq)
{
    DateTime? min = null;
    foreach(DateTime current in seq)
    {
        if (min == null || current < min.Value) 
            min = current;
    }
    return min;
}

在那里,它返回序列中最早的日期,如果序列为空则返回null。没有搞乱Where和谓词和变异的闭包以及所有那些废话:编写代码是直截了当的,显然是正确的。

答案 1 :(得分:10)

您的代码应该可以运行,但这是一种不必要的复杂方法。

尝试

this.Aggregate((min, next) => next < min ? next : min);