我应该先将参数值分配给局部变量而不是直接使用它们

本文关键字:参数 分配 局部变量 我应该 | 更新日期: 2023-09-27 18:31:58

是否有任何理由将参数值分配给方法中的局部变量,以便在不更改它们的情况下使用这些值? 即如下所示:

private void MyMethod(string path)
{
    string myPath = path;
    StreamReader mystream = new StreamReader(myPath);
    ...
}

或者我可以一直这样说(上面的代码是多余的,只是不干净):

private void MyMethod(string path)
{
    StreamReader mystream = new StreamReader(path);
    ...
}

我知道它是双向的,但我想确保在我的理解中没有遗漏任何东西。

我应该先将参数值分配给局部变量而不是直接使用它们

唯一需要执行此操作(本地分配)的情况是在 foreach 循环中或使用 Linq 时。 否则,您可能会遇到修改闭包的问题。

下面是来自 MSDN 博客的片段(下面的所有内容都来自链接)。

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

但我正在超越自己。这个片段的输出是什么?

var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach(var v in values) 
  funcs.Add( ()=>v );
foreach(var f in funcs) 
  Console.WriteLine(f());

大多数人期望它是100/110/120。 它实际上是120/120/120。为什么?

因为 ()=>v 表示"返回变量 v 的当前值",而不是"返回创建委托时返回的值 v"。闭包关闭于变量,而不是值。当方法运行时,显然分配给 v 的最后一个值是 120,因此它仍然具有该值。

这是非常令人困惑的。编写代码的正确方法是:

foreach(var v in values) 
{
  var v2 = v;
  funcs.Add( ()=>v2 );
}

现在会发生什么?每次重新启动循环体时,我们都会在逻辑上创建一个新的新变量 v2。每个闭包在不同的 v2 上关闭,该 v2 仅分配给一次,因此它始终保持正确的值。

基本上,问题出现是因为我们指定 foreach 循环是

 {
    IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
    try
    { 
      int m; // OUTSIDE THE ACTUAL LOOP
      while(e.MoveNext())
      {
        m = (int)(int)e.Current;
        funcs.Add(()=>m);
      }
    }
    finally
    { 
      if (e != null) ((IDisposable)e).Dispose();
    }
  }

如果我们指定扩展是

try
{ 
  while(e.MoveNext())
  {
    int m; // INSIDE
    m = (int)(int)e.Current;
    funcs.Add(()=>m);
  }

然后代码将按预期运行。

这是完全相同的事情,唯一的区别是在第一种情况下,您制作了引用的副本(当方法超出范围时,无论如何都会销毁,这发生在执行结束时)。

为了获得更好的可读性,请坚持使用第二种情况。

我更喜欢第二种选择。使用该参数创建新变量是没有意义的。此外,从阅读的角度来看,从路径(您收到的路径)创建流比实例化"myPath"变量更有意义。