闭包与匿名函数

什么是闭包?
所谓的闭包指的是:一个方法除了能和传递给他的参数交互之外,还可以和上下文进行更大成都的互动,但是闭包并不是C#语言独有的,事实上,这是一个很早的概念,而且现在很多驻留的编程语言都接纳了这个概念,如果要真正理解闭包要理解两个概念
1.外部变量:或者称为匿名方法的外部变量,指的是定义了一个匿名方法的作用域内(方法内部的)局部变量或者参数,对匿名方法来说是外部变量
2.捕获的外部变量:即在匿名方法内部使用的外部变量,也就是局部变量在匿名方法内部便是一个捕获的外部变量
知道这两种概念之后,再结合闭包的定义,可以发现,在闭包中出现的方法在c#中便是匿名方法,那这样有什么好处呢?实际上在游戏开发中,如果使用了闭包与匿名函数的结合,就可以不必专门设置额外的类型来储存已经知道的数据,直接使用上下文,这样的话提供了很大的便利性,如下:

void Start()
    {
        this.EnclosingFunction(999);
    }

    private void EnclosingFunction(int number)
    {
        int count = 100;
        string capturedOuterValue = "hello word";
        Action<int> action = delegate (int obj)
        {
            string str = "捕获了外部变量" + capturedOuterValue + number.ToString();
            Debug.Log(str);
        };

        action(0);
        if(number== 100)
        {
            //由于在这个作用域内没有声明匿名方法
            //因此notcapturevalue不是外部变量
            int notcapturevalue = 1000;
            Debug.Log(notcapturevalue);
        }
    }

上例便解视了什么是外部变量,什么是捕获的外部变量,什么不是外部变量
但是我们要明确一点,所谓的捕捉变量的背后所发生的的确是针对 变量 而言的,而不仅仅是获取变量所保存的值,思考一下,这样会产生什么样的后果呢?那就是被捕捉的变量的存货周期可能比作用域要长,如下

void Start()
    {
        this.EnclosingFunction(999);
    }

    private void EnclosingFunction(int number)
    {
        int outerValue = 100;
        string capturedouterValue = "hello world";
        Action<int> action = delegate (int obj)
        {
            string str = "捕获了外部变量" + capturedouterValue + number.ToString();
            Debug.Log(str);
            capturedouterValue = "hello world 你好世界";
        };
        capturedouterValue = "你好世界";
        action(0);
        Debug.Log(capturedouterValue);
    }

输出结果
在这里插入图片描述
仔细看一下上面的代码,分析一下,首先capturedouterValue是被捕获的外部变量,而这里

Action<int> action = delegate (int obj)
        {
            string str = "捕获了外部变量" + capturedouterValue + number.ToString();
            Debug.Log(str);
            capturedouterValue = "hello world 你好世界";
        };

仅仅是进行了将声明的匿名方法赋值给委托实例,并没有调用,然后直接修改变量captureouterValue为你好世界,通过打印打印出了捕获外部变量…然后直接修改captureouterValue的值,委托实例返回以后,再次打印发现打印的结果为hello world 你好世界,由此可以看出通过匿名方法船舰的委托实例不是读取变量,并且将其值保存下来,而是直接操作该变量,这究竟有什么意义?
再来看下面的例子

private int heroCount;
    private int SolderCount;
    // Start is called before the first frame update
    void Start()
    {
        List<int> list = new List<int>();
        list.Add(1);
        list.Add(2);
        list.Add(2);

        List<int> vs = this.FindAllLowAttack(list, 50f);
    }

    private List<int> FindAllLowAttack(List<int> list, float v)
    {
        if(list == null)
        {
            return null;
        }
        else
        {
            return list.FindAll(delegate (int a)
            {
                return a < v;
            });
        }
    }

这样的话在FindAllLowAttack方法中传入的float类型的参数v被匿名方法捕获,由于捕获的是变量本身,因此我们就获得了使用参数的能力,而不是在匿名函数中写死一个确定的数值来进行比较,这样的话代码会很简介,精巧。

之前说过将匿名方法赋值给一个委托实例的时候,并不会例可执行这个匿名方法,而是当这个委托被调用的时候才会触发,那么新的问题来了,如果创建了这个被捕获的外部变量的方法返回后,这个变量的生命周期是会随着方法的返回结束呢还是?
来看下面的例子

private int heroCount;
    private int SolderCount;
    // Start is called before the first frame update
    void Start()
    {
        Action<int> list = this.FindAllLowAttack();
        list(10);
        list(100);
        list(1000);
    }

    private Action<int> FindAllLowAttack()
    {
        int count = 0;
        Action<int> action = delegate (int a)
        {
            count += a;
            Debug.Log(count);
        };
        action(1);
        return action;
    }

输出结果:
在这里插入图片描述
从结果上可以看到,被捕获的变量的生命周期并没有因为调用方法的返回而结束,通过反编译得知,这个被捕获的变量count并没有分配在FindAllLowAttack方法对应的栈上,而是被存放在了另一个临时类中,那么FindAllLowAttack又是如何来对他的局部变量进行操作的呢?原因是FindAllLowAttack方法保留了对那个临时类的引用,通过类型的实例操作count变量,所以,在使用闭包和匿名函数的时候,一定要注意这个

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值