• 闭包(C#)


    通常来讲,大家一听到闭包,应该首先会想到JavaScript中的闭包,而不会想到C#中的闭包,但是C#中也是有闭包的,下面就让我来为大家仔细讲解讲解。

    在C#中,我们通常知道变量作用域有三种:1、是属于类的,我们常称之为field,2、是属于函数的,我们通常称之为局部变量,3、其实也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种我们同样称之为局部变量。

    这三种变量的生命周期都属于它所寄存的对象,变量是随着寄存对象的销毁而消亡。

    三种作用域可以这样理解:类中的变量是随着类实例化而产生,同时伴随着类对象资源回收而消亡,类中的static和const对象除外;函数的变量随着函数的调用开始而产生,伴随着函数执行结束而结束,函数内部的变量生命周期满足先进后出的特点;

    闭包是使用的变量已经脱离其作用域,由于变量和作用域之间存在上下文关系,从而可以在当前环境中继续使用,即上下文环境所定义的一种函数对象。

    C#中,闭包允许你将一些行为进行封装,将这个封装的行为当成对象进行传递,但是它能够访问到最初声明时的上下文。

    首先我们看一个最简单的javascript中经常见到的关于闭包的例子:

    function f1(){
     var n=999;
     return function(){
        alert(n); // 999
     return n;
      }
    }
     var a =f1();
     alert(a()); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    将上面的Java Script代码翻译成C#代码如下:

    public class TCloser
     {
     public Func<int> T1()
     {
         var n = 999;
         return () =>
         {
             Console.WriteLine(n);
             return n;
         };
     }
     }
      
     class Program{
         static void Main(){
         var a =new TCloser();
         var b = a.T1();
         Console.WriteLine(b());
       }
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    从上面的代码可以看出,变量n是属于函数T1的局部变量,n的生命周期应该是伴随着函数T1的调用结束而结束,但却在返回的委托b中仍能够调用,这正是C#闭包所展示出的威力,其实闭包就相当于委托。

    当T1调用返回的匿名委托时,编译器会判断这是合法的,返回的委托b和函数T1存在上下文关系,匿名委托是允许使用它所在的函数或类中的局部变量,从而编译器通过一系列动作使调用的函数T1的局部变量自动闭合,该局部变量满足新的作用范围。

    讨论一下C#中的闭包

    1、静态全局字段

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication3
    {
        class Program
        {
            public static int copy;//[0]这个不是闭包
            static void Main()
            {
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    copy = counter;
                    actions.Add(() => Console.WriteLine(copy));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
        }
    }
    
    //注:Action定义如下:
    //public delegate void Action();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2、局部变量(闭包一)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication3
    {
        class Program
        {
            static void Main()
            {
                int copy;//[1]闭包一
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    copy = counter;
                    actions.Add(() => Console.WriteLine(copy));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
        }
    }
    
    //注:Action定义如下:
    //public delegate void Action();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    3、局部变量(闭包二)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication3
    {
        class Program
        {
            static void Main()
            {
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    int copy;//[1]闭包二
                    copy = counter;
                    //int copy = counter;//换种写法
                    actions.Add(() => Console.WriteLine(copy));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
        }
    }
    
    //注:Action定义如下:
    //public delegate void Action();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    4、局部变量(闭包三)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication3
    {
        class Program
        {
            static void Main()
            {
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)//[3]闭包三
                {
                    actions.Add(() => Console.WriteLine(counter));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
        }
    }
    
    //注:Action定义如下:
    //public delegate void Action();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    1:输出什么?
    2:输出什么?
    3:输出什么?
    4:输出什么?

    这几个例子,可以将匿名函数进行转换,这样可以看的更清楚。

    在1中,“外部变量”copy是类的一个静态成员,因此可以讲匿名函数转换为以下形式:

    class Program
        {
            public static int copy;//[0]这个不是闭包
            static void TempMethod()
            {
                Console.WriteLine(copy);
            }
            static void Main()
            {
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    copy = counter;
                    actions.Add(new Action(TempMethod));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2和3中“外部变量”copy是Main方法中的局部变量,局部变量的生存期现在必须至少延长为匿名函数委托的生存期。这可以通过将局部变量“提升”到编译器生成的类的字段来实现。

    之后,局部变量的实例化对应于为编译器生成的类创建实例,而访问局部变量则对应于访问编译器生成的类的实例中的字段。而且,匿名函数将会成为编译器生成类的实例方法:

    class Program
        {
            static void Main()
            {
                //定义动作组
                TempClass tc = new TempClass();
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    tc.copy = counter;
                    actions.Add(tc.TempMethod);
                }
                //执行动作
                foreach (Action action in actions) action();
            }
            class TempClass
            {
                public int copy;
                public void TempMethod()
                {
                    Console.WriteLine(copy);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    class Program
        {
            static void Main()
            {
                //定义动作组
    
                //定义动作组
                List<Action> actions = new List<Action>();
                for (int counter = 0; counter < 10; counter++)
                {
                    TempClass tc = new TempClass();
                    tc.copy = counter;
                    actions.Add(tc.TempMethod);
                }
                //执行动作
                foreach (Action action in actions) action();
            }
            class TempClass
            {
                public int copy;
                public void TempMethod()
                {
                    Console.WriteLine(copy);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4中的“外部变量”counter是for循环的循环因子,因此可以转换为以下形式:

    class Program
        {
            static void Main()
            {
                //定义动作组
                List<Action> actions = new List<Action>();
                TempClass tc = new TempClass();
                for (tc.copy = 0; tc.copy < 10; tc.copy++)
                {
                    actions.Add(new Action(tc.TempMethod));
                }
                //执行动作
                foreach (Action action in actions) action();
            }
            class TempClass
            {
                public int copy;
                public void TempMethod()
                {
                    Console.WriteLine(copy);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    闭包就先讲解这么多,下次在更加深入的讲解,欢迎各位宝宝们留言。

  • 相关阅读:
    基于STC12C5A60S2系列1T 8051单片机的数模芯片DAC0832实现数模转换应用
    电压放大器原理(电压放大器适用于什么场合使用)
    C# 程序兼容同一个dll的不同版本
    Windows平台如何实现RTSP流二次编码并添加动态水印后推送RTMP或轻量级RTSP服务
    DEJA_VU3D - Cesium功能集 之 052-模拟卫星轨道(高空)效果
    接口设计的那些事
    Spring IoC容器
    鸿蒙应用开发之HTTP数据请求
    cs231n_1_IntroToConv
    【Vue】vue中v-if的用法
  • 原文地址:https://blog.csdn.net/shanniuliqingming/article/details/133688793