• Closure的思考


    什么是Closure?

    在我的这篇博客中谈到了代理的行为不是我期望的结果的疑问。经过学习理解发现是有关Closure的知识,Closure是在函数式编程比如F#,Haskell等中的一个重要的概念。Clousre是指外部变量被绑定在函数内部。这样理解起来可能比较抽象,用C#示例代码说起来可能比较容易理解,在C#中涉及到Closure的主要是匿名代理和lambda表达式等。首先看一个示例:
    using System;

    namespace Test
    {
       
    class Program
        {
           
    static Action GetPrinter()
            {
               
    int i = 0;
                Action action
    = () => Console.WriteLine(i);
               
    return action;
            }

           
    static void Main(string[] args)
            {
                Action action
    = GetPrinter();
                action();
            }
        }
    }
    上面的例子可以看出,在Main函数中可以通过action这个代理来访问到GetPrinter函数中的变量i的值,不但如此,任何对变量i值的改变都能影响到action这个代理。这在普通函数中是不可能达到的,因为函数之间是不能互相访问内部的。因此,在这篇博客中提到的问题也就容易理解了,问题就出在这段代码中:
    //Loops all the objects and creates operations.
    foreach (var myObject in myObjects)
    {
       
    //Instantiates an operation that printing my object's name.
        Action action = () => { Console.WriteLine(myObject.Name); };
        operators.Add(
    new Operation(action));
    }
    对于每一次循环而言,可以正确的生成一个Action,这个Action引用到了当前的循环变量myObject,然后当循环变量被替换到下个myObject时,已生成的Action也都指向了当前的myObject。以此类推,到最后所有的Action引用的都是最后一个myObject,也就造成了文中所述的结果。或许你认为这是Closure的一个缺点,或者是一个特有的功能。不管如何,Closure的行为就是这样,了解了它的行为我们便可在今后的开发中避免一些不必要的错误。
    那么有个疑问是,编译器是如何构造Closure的呢?
    对于Closure,C#编译器会为其构造出一个类来,类中的某个函数即为用户定义的代理(比如本文中的Action action),而此代理所引用的的变量(专指来自于所定义代理的环境中的变量,比如本文中的 int i)则变成编译器所构造出来的类的某一个成员。也就是通过这样的行为,编译器成功的实现了Closure。对于本文中的例子,编译器为其自动生成的的类可以这样描述:
    class ActionClosure
    {
       
    public int i = 0;
       
    public void action()
        {
            Console.WriteLine(i);
        }
    }
    当然了,类的名字不是上面所描述的,而是编译器给的一个特殊的名字以防止和用户定义的类命名冲突。此处用这个名字只是为了看起来更清晰。 任何对原有i值的改变都会影响到ActionClosure.i的值。

    例如下面的代码:

                int i = 0;
                Action action
    = () => Console.WriteLine(i); 
                i = 1;
    会导致ActionClosure发生如下代码所示的行为:
                ActionClosure closure = …
                closure.i = 0;
                closure.i = 1;            

    本文参考了这篇很不错的文章:http://diditwith.net/2007/02/09/WhatsInAClosure.aspx,本文所引用的内容版权归原作者所有。

  • 相关阅读:
    函数式编程
    scala 有 + 运算符吗?
    使用 Idea 打 scala程序的 jar 包
    相见恨晚的 scala
    半夜思考,为什么 String 具有不变性
    我的常用
    DataTable学习笔记
    Js 操作cookie
    嵌套的 ajax 请求
    Jquery插件收集【m了慢慢学】
  • 原文地址:https://www.cnblogs.com/lsp/p/1643565.html
Copyright © 2020-2023  润新知