c# - 为什么我的 INT 变量是通过引用传递的? C#

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

好吧,我知道(我认为)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 类型的新委托(delegate)“a”并将其分配给将“x”写入控制台的匿名方法。 然后将“x”赋值为2。最后,调用“a”并将“x”的值打印到控制台。

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

最佳答案

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

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

重要的区别是它绑定(bind)到变量,而不是。换句话说,当声明“a”时,不会复制“x”的值。相反,使用对“x”的“引用”,以便“a”将始终使用“x”的最新值。事实上,即使“x”超出范围,对“x”的“引用”也将保留。当您编译程序时,编译器会发挥一些“编译器魔法”并生成类似于以下代码片段的内容:

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.委托(delegate)“a”现在被分配给“SomeHandlerClosure”实例上“CompilerNamedMethod”的新委托(delegate)实例。

清澈如泥??

关于c# - 为什么我的 INT 变量是通过引用传递的? C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23098140/

相关文章:

c# - CA2000 将对象引用传递给 C# 中的基本构造函数

ios 将父级委托(delegate)指定为其子级之一

jquery - 使用 Ruby 和 JQuery 构建表单生成器

c# - 使用 moq 和 nunit 在 Action 结果 C# 中模拟类的方法

c# - 使用 StringFormat 在值中间添加单词

c# - 使用带有委托(delegate)函数的 IComparer<> 进行搜索

c# - 我可以只举办没有委托(delegate)的事件吗?

javascript - 自调用函数+闭包改善垃圾收集导致的挂起

javascript - 动态生成带有闭包的js按钮来分配onclick函数参数

c# - MySQL 相当于 MS SQL 代码