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

标签 c#

是否有任何理由将参数值分配给方法内的局部变量以便使用这些值而不更改它们? IE。像下面这样:

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 的当前值”,而不是“返回创建委托(delegate)时 v 返回的值”。闭包关闭变量,而不是值。当这些方法运行时,显然最后分配给 v 的值是 120,所以它仍然是那个值。

这很令人困惑。正确的代码写法是:

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

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

基本上,问题的出现是因为我们指定 foreach 循环是 for 的语法糖

 {
    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);
  }

然后代码将按预期运行。

关于c# - 我应该先将参数值分配给局部变量而不是直接使用它们吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10267858/

相关文章:

c# - 不显眼的验证 C# MVC Razor

c# - 使用 Json.NET 选择特定对象(SelectToken)

c# - 如何在 RichTextBox 中加载 .rtf 文件?

c# - 如何在不产生异常的情况下检查 COM 属性或方法是否存在?

c# - 从不同的代码隐藏更改 ascx 内容

c# - 如何从 TextBox 中删除重复项?

c# - 如何使用 jqGrid 和文本框进行搜索?

c# - 自动实现的属性是否支持特性?

c# - 如何使用 c# 2.0 WebClient 忽略证书错误 - 没有证书

c# - 从 c# 到 mongodb 正确插入 DateTime