代码之家  ›  专栏  ›  技术社区  ›  John Alexiou

为什么这两个函数不返回相同的值?

  •  5
  • John Alexiou  · 技术社区  · 14 年前

    考虑这个代码片段并尝试猜测 y1 y2 评估为

    static class Extensions
    {
        public static Func<T> AsDelegate<T>(this T value)
        {
            return () => value;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
    
        Program()
        {
            double x = Math.PI;
    
            Func<double> ff = x.AsDelegate();
            Func<double> fg = () => x;
    
            x = -Math.PI;
    
            double y1 = ff();  // y1 = 3.141..
            double y2 = fg();  // y2 = -3.141..
    
        }
    }
    

    复制 x . 但是当您将上面的内容更改为类的委托时,结果仍然不同。例子:

    class Foo
    {
        public double x;
    }
        Program()
        {
            Foo foo = new Foo() { x=1.0 };
    
            Func<Foo> ff = foo.AsDelegate();
            Func<Foo> fg = () => foo;
    
            foo = new Foo() { x = -1.0 };
    
            double y1 = ff().x;    // y1 = 1.0
            double y2 = fg().x;    // y2 = -1.0
        }
    

    所以这两个函数必须返回同一类的两个不同实例。有意思的是 ff() 带有对局部变量的引用 foo fg() 不是,它依赖于当前范围内的内容。

    所以当这两个委托被传递到代码的其他部分时,会发生什么情况 实例?不知何故,当扩展方法与委托相结合时,谁拥有哪些信息(数据)的问题变得越来越不清楚。

    7 回复  |  直到 14 年前
        1
  •  3
  •   Jeff Yates    14 年前

    ff 捕获(绑定)到 属于 x

    Func<double> ff = x.AsDelegate();
    

    相比之下, fg 变量

    Func<double> fg = () => x;
    

    所以,当 变化, 是不可信的,但是 前景 变化。

        2
  •  7
  •   Community THelper    7 年前

    AsDelegate 捕获变量 value (的参数 代理 ),而 () => x x . 所以如果你改变 价值 不过。

    见: Outer Variable Trap

        3
  •  4
  •   Marc Gravell    14 年前

    在AsDelegate中,我们捕获参数“value”。这个目标的值在方法被调用时被当作变量值的副本,并且永远不会改变——所以我们看到了原始对象。

    变量 foo(不是 价值

    基本上,添加一个方法调用改变了被捕获的大腿。

        4
  •  3
  •   Jeff Yates    14 年前

    AsDelegate 扩展方法使用 x 代理 调用,而lambda表达式 () => x 捕获变量 因此

        5
  •  2
  •   Andrew Bezzub    14 年前

    () => x 捕获x值。编译器创建特殊类来处理lambda或匿名委托,捕捉lambda中使用的任何变量。

    例如,如果运行以下代码:

        List<Func<int>> list = new List<Func<int>>();
    
        for (int i = 0; i < 5; i++)
        {
            list.Add(() => i);
        }
    
        list.ForEach(function => Console.WriteLine(function()));
    

    你会看到打印出来的数字是一样的。

        6
  •  2
  •   jason    14 年前

    你需要了解 closures in C# . 另见 Wikipedia .

        7
  •  0
  •   codymanix    14 年前

    新代表 到一个给定的函数并存储它。如果稍后覆盖foo,则不会触及新创建的委托。

    兰姆达 表达方式 /*捕获*它的上下文,这意味着foo变量。lambda表达式可以看到对lambda表达式提升的变量所做的所有更改。