一. 引子

先来看如下代码:

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#委托内部使用局部的变量的问题的更多相关文章

  1. C# 中的局部static变量

    其实这问题没什么可讨论的,C#不支持局部静态变量. 但还是想了一下C#为什么不支持局部静态变量,以下均是个人想法. C++和C支持局部静态变量,也就是在一个函数的内部声明一个静态变量,这种变量的特定如 ...

  2. c# 委托内部构造

    以下纯属个人简介,错误之处,请随意指出. 委托是指向方法的,而事件是委托的触发器,执行事件,就会遍历委托里的方法,并且执行. 委托内部构造第一块是方法指针(methodPtr),用于指向方法的内存地址 ...

  3. C++中的局部变量、全局变量、局部静态变量、全局静态变量的区别

    局部变量(Local variables)与 全局变量: 在子程序或代码块中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量. 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序 ...

  4. (八)Activiti之流程变量和局部流程变量

    一.流程变量 1.1 概念 如果,当流程走到"学生请假"这个任务节点的时候,此时可以用TaskService设置流程变量,变量值包含请假人.请假时间.请假理由等信息,这些信息存在表 ...

  5. test dword ptr [eax],eax ; probe page.局部数组变量定义所分配的最大空间为1M

    问题的出现 使用VS2017编写程序时,程序编译可以通过,但运行时就会弹出错误 经过查证发现: 这跟局部数组变量定义所分配的最大空间设置大小有关. 局部变量的申请空间是存放于栈中,windows里默认 ...

  6. 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 ...

  7. Java基础-内部类-为什么局部和匿名内部类只能访问局部final变量

    先看下面这段代码: public class Test { public static void main(String[] args) { } public void test(final int ...

  8. [ python ] 全局和局部作用域变量的引用

    全局与局部变量的引用 (a)locals(b)globals 这里还需要在补充2个关键字一起比较学习,关键字:(c)nonlocal(d)global locals 和 globals locals: ...

  9. C++函数返回局部指针变量

    遇到过好几次关于函数返回指针变量问题,有时候是可以的,有时候是不可以的,然后就混乱了.今天研究了下,结果发现原来和内存分配有关. 用下面的例子分析下吧: char * test() { char a[ ...

随机推荐

  1. 以太坊智能合约开发 Solidity学习

    1. pragma solidity >=0.4.22 <0.6.0;//版本号,头文件 contract BooleanTest { bool _a;//默认返回false int nu ...

  2. ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架

    前面介绍了很多ABP系列的文章,一步一步的把我们日常开发中涉及到的Web API服务构建.登录日志和操作审计日志.字典管理模块.省份城市的信息维护.权限管理模块中的组织机构.用户.角色.权限.菜单等内 ...

  3. Erlang/Elixir精选-第3期(20191216)

    2019年,Erlang社区在应用层上除了aeternity区块链让人印象深刻(Killer App)外,就没有特别出彩的应用出现.在Web,IoT,MessageQueue这些成熟领域,已经有相当成 ...

  4. ubuntu上的安装.netcore2.1

    .net core 在ubuntu上安装比较容易,依次执行正面语句即可 sudo apt-get install curl curl https://packages.microsoft.com/ke ...

  5. Bootstrap基本CSS样式

    一.简介.使用 1.简介 Bootstrap 来源于 Twitter,是一款基于 Html.Css.JavaScript 的前端UI框架.可以方便.快速的开发web界面. 教程:https://www ...

  6. JS中的call,apply和bind及记忆方式

    总结 call().apply()和bind()都是用来改变函数执行时的上下文,可借助它们实现继承:call()和apply()唯一区别是参数不一样,call()是apply()的语法糖:bind() ...

  7. Vulnhub DC-1靶机渗透学习

    前言 之前听说过这个叫Vulnhub DC-1的靶机,所以想拿来玩玩学习,结果整个过程都是看着别人的writeup走下来的,学艺不精,不过这个过程也认识到,学会了很多东西. 所以才想写点东西,记录一下 ...

  8. iOS与JS交互-WKWebView

    iOS移动端应用中,一般都会嵌入网页,在网页中处理的结果需要反馈给iOS让移动端做出相应的处理(例如页面跳转), 1.webview的配置 2.代理方法中拿到事件进行处理 二.JS中的配套出发事件 当 ...

  9. OSI参考模型总结

    OSI(Open System Interconnect),即开放式系统互联. 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互连模型.下面我们将一层一层的看OSI协议. ...

  10. Python踩坑系列之使用redis报错:module 'redis' has no attribute 'Redis'问题

    初次使用redis时,在链接Redis后,运行报错“module 'redis' has no attribute 'Redis' ”. 具体代码如下: import redis r = redis. ...