【问题标题】:Why is my INT variable being passed by reference?? C#为什么我的 INT 变量是通过引用传递的? C#
【发布时间】:2014-04-16 02:50:14
【问题描述】:

好的,所以我知道 C# 中值类型和引用类型之间的区别(我认为)。但是,下面的代码并不像我所期望的那样运行,因为我知道我对值类型和引用类型的了解。

delegate void SomeHandler();

static Action GetSomeHandler()
 {
   int x = 1;

   SomeHandler a = delegate { Console.WriteLine(x); };

   x = 2;

   return a;
 }

static void Main(string[] args)
{
   SomeHandler a = GetSomeHandler();

   a();
}

我很困惑,因为在我的 GetSomeHandler 方法中声明了一个局部变量“x”并将其初始化为 1。然后,声明 SomeHandler 类型的新委托“a”并将其分配给将“x”写入控制台的匿名方法。 然后将“x”分配给 2。最后,调用“a”并将“x”的值打印到控制台。

我希望输出为 1,因为“x”是一个 int(值类型),并且我假设当 2 分配给“x”时,它不会影响我在委托中使用的内容,因为该值会被复制而不是指向内存中的相同位置,但实际输出为 2!为什么!?

【问题讨论】:

  • 代表不同。您实际上指的是委托内部和外部的同一个变量x。这会导致很多与 for 循环的混淆:blogs.msdn.com/b/ericlippert/archive/2009/11/12/…
  • @EdS。你能详细说明一下吗?
  • @Blorgbeard 为什么代表会有所不同?
  • @Jason 的回答很好地解释了这一点。您没有将x 传递 给委托人——委托人只是引用变量x,就像x = 2; 一样。由于委托代码是在 x 设置为 2 之后执行的,所以这就是您看到的值。

标签: c# delegates closures pass-by-reference value-type


【解决方案1】:

啊,你发现了闭包的魔力!

您认为“x”是一种值类型并且它不应该以这种方式表现是正确的;也就是说,如果您没有在方法中声明匿名函数。匿名方法是一个闭包,并且绑定到它的父方法体和其中的局部变量(在这种情况下,它是局部变量“x”和父方法 GetSomeHandler)。

重要的区别是它绑定到变量,而不是。换句话说,当声明“a”时,不会复制“x”的值。相反,使用对“x”的“引用”,以便“a”将始终使用“x”的最新值。事实上,即使“x”超出范围,对“x”的“引用”也会被保留。当你编译你的程序时,编译器会运行它的一些“编译器魔法”,并生成类似于以下 sn-p 代码的东西:

delegate void SomeHandler();

// This is the helper class generated by the compiler that allows an anonymous function inside your method access to local variables even after the function or method has returned.
sealed class SomeHandlerClosure
{
   public int x;

   public void CompilerNamedMethod()
   {
     Console.WriteLine(x);
   }
}

static SomeHandler GetSomeHandler()
 {
   SomeHandlerClosure closure = new SomeHandlerClosure();
   closure.x = 1;

   SomeHandler a = new SomeHandler(closure.CompilerNamedMethod);

   closure.x = 2;

   return a;
 }

static void Main(string[] args)
 {
   SomeHandler a = GetSomeHandler();

   a();
 }

“GetSomeHandler”方法真的是神奇的地方:

1.在方法的开始,“SomeClosure”类的一个实例被创建。 (请注意,为了清楚起见,我选择使用名称“SomeHandlerClosure”和“CompilerNamedMethod”。实际上,编译器会生成名称以防止名称冲突。)

2.“GetSomeHandler”方法中对局部变量“x”的所有引用都已替换为“SomeHandlerClosure”实例中对“x”字段的引用。

3.委托“a”现在分配给“SomeHandlerClosure”实例上的“CompilerNamedMethod”的新委托实例。

清如泥??

【讨论】:

    猜你喜欢
    • 2019-05-10
    • 1970-01-01
    • 2013-08-22
    • 2016-12-09
    • 2014-01-13
    • 2016-08-16
    • 2017-01-20
    • 2012-10-15
    • 1970-01-01
    相关资源
    最近更新 更多