C#委托内部使用局部的变量的问题
一. 引子
先来看如下代码:
int i = ;
Action action1 = () =>
{
Console.WriteLine("打印一下i的值:" + i);
};
i = ;
Action action2 = () =>
{
Console.WriteLine("打印一下i的值:" + i);
};
action1.Invoke();
action2.Invoke();
那么问题来了,执行这两个委托之后,输出的是
打印一下i的值:0
打印一下i的值:1
还是
打印一下i的值:1
打印一下i的值:1
相信有一些人会觉的是第一种情况,但是实际的结果却是第二种情况的结果,如下图测试结果,这是为什么呢?

二. 原理部分
首先我们会议一下C#中委托究竟是什么?
相信很多的小伙伴都知道,委托是一种语法糖。它在编辑之后,编译器会为其生成一个类。我们使用 ILSpy.exe 来看一下编译之后生成的是什么:
ILSpy.exe 打开,如下图:

这个自动生成的类 "<>c__DisplayClass0_0" 便是委托生成的类,看一下IL代码:
.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass0_0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
)
// Fields
.field public int32 i // Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x20a0
// Code size 8 (0x8)
.maxstack IL_0000: ldarg.
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method '<>c__DisplayClass0_0'::.ctor .method assembly hidebysig
instance void '<Main>b__0' () cil managed
{
// Method begins at RVA 0x20a9
// Code size 29 (0x1d)
.maxstack // (no C# code)
IL_0000: nop
// Console.WriteLine("打印一下i的值:" + this.i);
IL_0001: ldstr "打印一下i的值:"
IL_0006: ldarg.
IL_0007: ldfld int32 Demo.Program/'<>c__DisplayClass0_0'::i
IL_000c: box [mscorlib]System.Int32
IL_0011: call string [mscorlib]System.String::Concat(object, object)
IL_0016: call void [mscorlib]System.Console::WriteLine(string)
// (no C# code)
IL_001b: nop
IL_001c: ret
} // end of method '<>c__DisplayClass0_0'::'<Main>b__0' .method assembly hidebysig
instance void '<Main>b__1' () cil managed
{
// Method begins at RVA 0x20a9
// Code size 29 (0x1d)
.maxstack // (no C# code)
IL_0000: nop
// Console.WriteLine("打印一下i的值:" + this.i);
IL_0001: ldstr "打印一下i的值:"
IL_0006: ldarg.
IL_0007: ldfld int32 Demo.Program/'<>c__DisplayClass0_0'::i
IL_000c: box [mscorlib]System.Int32
IL_0011: call string [mscorlib]System.String::Concat(object, object)
IL_0016: call void [mscorlib]System.Console::WriteLine(string)
// (no C# code)
IL_001b: nop
IL_001c: ret
} // end of method '<>c__DisplayClass0_0'::'<Main>b__1' } // end of class <>c__DisplayClass0_0
我们可以看到,这个自动生成的类中,它有两个方法 "<Main>b__0" 和 "<Main>b__1" ,分别对应的是委托 action1 和 action2,于此同时还有一个 public 的成员字段 i,用来保存外部传递的 i 的值。很明显,由于第二次的赋值,把 i 赋值成了1,所以会导致两个方法的 i 的值因为共用一个成员变量而都成为1。
三. 解决方案
那么问题来了,我们怎么样才能用局部的临时变量同时赋值给两个委托而对它们不会造成互相影响呢?
1. 设置连个不同的临时变量(感觉有点笨)
程序代码:
int i = ;
Action action1 = () =>
{
Console.WriteLine("打印一下i的值:" + i);
};
int j = ;
Action action2 = () =>
{
Console.WriteLine("打印一下i的值:" + j);
};
action1.Invoke();
action2.Invoke();
2. 将临时变量的值按照委托的参数传入
程序代码:
Action<int> action1 = (num) =>
{
Console.WriteLine("打印一下i的值:" + num);
};
action1.Invoke();
action1.Invoke();
C#委托内部使用局部的变量的问题的更多相关文章
- C# 中的局部static变量
其实这问题没什么可讨论的,C#不支持局部静态变量. 但还是想了一下C#为什么不支持局部静态变量,以下均是个人想法. C++和C支持局部静态变量,也就是在一个函数的内部声明一个静态变量,这种变量的特定如 ...
- c# 委托内部构造
以下纯属个人简介,错误之处,请随意指出. 委托是指向方法的,而事件是委托的触发器,执行事件,就会遍历委托里的方法,并且执行. 委托内部构造第一块是方法指针(methodPtr),用于指向方法的内存地址 ...
- C++中的局部变量、全局变量、局部静态变量、全局静态变量的区别
局部变量(Local variables)与 全局变量: 在子程序或代码块中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量. 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序 ...
- (八)Activiti之流程变量和局部流程变量
一.流程变量 1.1 概念 如果,当流程走到"学生请假"这个任务节点的时候,此时可以用TaskService设置流程变量,变量值包含请假人.请假时间.请假理由等信息,这些信息存在表 ...
- test dword ptr [eax],eax ; probe page.局部数组变量定义所分配的最大空间为1M
问题的出现 使用VS2017编写程序时,程序编译可以通过,但运行时就会弹出错误 经过查证发现: 这跟局部数组变量定义所分配的最大空间设置大小有关. 局部变量的申请空间是存放于栈中,windows里默认 ...
- c++ 反汇编 局部静态变量
vs2017下测试 34: for (int i = 0; i < 5; i++) 0029734E C7 45 F8 00 00 00 00 mov dword ptr [ebp-8],0 0 ...
- Java基础-内部类-为什么局部和匿名内部类只能访问局部final变量
先看下面这段代码: public class Test { public static void main(String[] args) { } public void test(final int ...
- [ python ] 全局和局部作用域变量的引用
全局与局部变量的引用 (a)locals(b)globals 这里还需要在补充2个关键字一起比较学习,关键字:(c)nonlocal(d)global locals 和 globals locals: ...
- C++函数返回局部指针变量
遇到过好几次关于函数返回指针变量问题,有时候是可以的,有时候是不可以的,然后就混乱了.今天研究了下,结果发现原来和内存分配有关. 用下面的例子分析下吧: char * test() { char a[ ...
随机推荐
- C#冒泡算法
冒泡算法:先看代码吧,我不喜欢先说一大堆,看不懂了再说 class Program { static void Main(string[] args) { , , , , , }; ; i <a ...
- JavaScript 引擎 V8 执行流程概述
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/t__Jqzg1rbTlsCHXKMwh6A作者:赖勇高 本文主要讲解的是V8的技术,是V8的入 ...
- SSH框架之Hibernate第四篇
Hibernate中有两套实现数据库数据操作的方式 : hibernate前3天讲解的都是 : hibernate自己的操作方式(纯XML配置文件的方式) 另一种方式是基于JPA的操作方式(通过注解的 ...
- Java面试基础 -- Git篇
1.Git和SVN有什么区别? Git SVN Git是一个分布式的版本控制工具 SVN 是集中版本控制工具 它属于第3代版本控制工具 它属于第2代版本控制工具 客户端可以在其本地系统上克隆整个存储库 ...
- 【转载】Android App应用启动分析与优化
前言: 昨晚新版本终于发布了,但是还是记得有测试反馈app启动好长时间也没进入app主页,所以今天准备加个班总结一下App启动那些事! app的启动方式: 1.)冷启动 当启动应用时,后台没有该应用 ...
- python 部署lvs
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ...
- udp协议,进程(同步,异步)
udp协议与进程 一.udp协议 QQ聊天室 #- sever import socket #socket.SOCK_DGRAM--->UPD协议 sever = socket.socket(t ...
- MySQL 优化 (四)
参数优化 innodb_log_buffer_size 此参数确定些日志文件所用的内存大小,以M为单位.缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小. innodb_log_buffer_s ...
- Python—创建进程池的方式
创建进程池 from multiprocessing import Pool import time,os result = [] # 存放所有worker函数的返回值 def worker(msg) ...
- Tcp连接和断开
三次握手:客户端为a,服务端为b:开始都是closed状态:a主动打开进入到syn_sent状态,b被动打开进入listen状态:第一次握手,a向b发送SYN=1,seq为x的包,b收到以后进入syn ...