学习笔记——C#的闭包

Lambda表达式

Lambda表达式实际上是简化委托的写法,只要有委托参数类型的地方,就可以使用Lambda表达式表示。

// 委托写法
Action<int, int> act = Add;

public static void Add(int a,int b)
{
    Console.WriteLine(a + b);
}
// Lambda表达式写法
Action<int, int> act = (a, b) => Console.WriteLine(a + b);

Lambda表达式原理

Lambda表达式是语法糖,根据是否使用类的成员可以分为几种情况讨论

使用类的成员
  • 使用类的成员,就不会生成匿名类,而是生成一个内部方法
  • 这个内部方法会有一个地址指向类的成员
引用方法内部的局部变量

这种情况下会生成一个匿名类。编译器在背后生成私有的密封类<>c,其中 <>9_5_0 定义委托静态变量,委托的逻辑写到了这个类的Addb_5_0函数里面。Lambda表达式所在类中所有非闭包的代码都生成到<>c类中。

[Serializable]
 [CompilerGenerated]
 private sealed class <>c
 {
     ……
     public static Action<int, int> <>9__5_0;
​
     internal void <.ctor>b__5_0(int a, int b)
     {
        Console.WriteLine(a + b);
     }
     ……
 }
​
private Action<int, int> act = <>c.<>9__5_0 ?? (<>c.<>9__5_0 = new Action<int, int>(<>c.<>9.<.ctor>b__5_0));

闭包

Lambda表达式可以在委托方法中,跨作用域访问类的成员、函数的局部变量、函数的入参。 

class Test
{
    int count = 0;
    public void M() 
    {
        List<Action> lists = new List<Action>();
​    
        for (int i = 0; i < 5; i++)
        {
            lists.Add(() => { Console.WriteLine($"{i}:{count++}"); });
        }
​
        foreach (var v in lists) 
        {
            v();
        }        
    }
}

在调用v()时,局部变量i本已经释放,但仍能打印出结果,于是可以说,闭包延长了变量的生命周期。

分析结果

原理如下:

很直观的看到使用for循环时,只创建了一个对象 <>c_DisplayClass1_0,循环使用的变量是 <>c_DisplayClass0.i,局部变量i同时被5个闭包引用,这5个闭包共享i,所以最后他们打印出来的值是一样的,都是i最后退出循环时候的值5。

public class Test
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass1_0
    {
        
        public Test <>4__this; // 引用的是对象,而非成员变量

        public int i;
​
        internal void <M>b__0()
        {
            Test test = <>4__this;
            int count = <>4__this.Count;    
            int i = this.i;                
            test.Count = count + 1;
            Console.WriteLine($"{i}:{count}");    
        }
    }
    
    private int Count = 0;
​
    private void M()
    {
        List<Action> lists = new List<Action>();
        <>c__DisplayClass1_0 <>c__DisplayClass1_ = new <>c__DisplayClass1_0();  // 循环只定义一个对象
        <>c__DisplayClass1_.<>4__this = this;
        <>c__DisplayClass1_.i = 0;
        while (<>c__DisplayClass1_.i < 5)
        {
            lists.Add(new Action(<>c__DisplayClass1_.<M>b__0));
            <>c__DisplayClass1_.i++;
        }
        foreach (Action v in lists)
        {
            v();
        }


    }

}

 如代码所示,在最后调用委托时,<>c_DisplayClass1_.i值已经是5了,而此时test.count的值还是0,每调用一次委托就会输出一次i的值5和count逐渐加1的值

参考:C#闭包详解_c# 闭包_猫不在的博客-CSDN博客

        【精选】C#:深入理解 lambda表达式与闭包_lambda表达式闭包-CSDN博客

工具:ILDasm.exe

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值