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

时间:2010-11-09 17:59:48

标签: c# delegates extension-methods

考虑此代码段并尝试猜测y1y2评估为

的内容
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..

    }
}

您可能会说-Aha- double是一种值类型,因此扩展方法返回的值是主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()没有,它依赖于当前范围内的内容。

那么当这两个代理被传递给代码的其他部分时,会发生什么呢?这些部分没有foo实例的可见性?不知何故,当扩展方法与代表合并时,谁拥有什么信息(数据)变得越来越不清楚。

7 个答案:

答案 0 :(得分:7)

AsDelegate捕获变量valueAsDelegate的参数),而() => x捕获变量x。因此,如果更改x的值,则lambda表达式将返回不同的值。但是,更改x不会更改value

请参阅:Outer Variable Trap

答案 1 :(得分:4)

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

直接lambda捕获变量 foo(不是变量的 - 变量本身) - 因此我们看到变化此

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

答案 2 :(得分:3)

AsDelegate扩展方法在调用x时使用AsDelegate的值,而lambda表达式() => x捕获变量x,因此x的值是在调用表达式时(而不是定义它时的值)。

答案 3 :(得分:3)

ff在此行捕获(绑定)x

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

相比之下,fg在此行与变量 x绑定:

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

因此,当x的值发生变化时,ff未被感染,但fg发生变化。

答案 4 :(得分:2)

() => x捕获x值。编译器创建特殊类来处理lambdas或匿名委托,捕获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()));

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

答案 5 :(得分:2)

您需要了解closures in C#。另请参阅Wikipedia

答案 6 :(得分:0)

第一种方法为给定的函数创建新委托并存储它。你以后覆盖了foo,你的新创建的代表没有被触及。

第二个是 lambda 表达式,提升 / *捕获*它的上下文,这意味着foo变量。对lambda表达式提升的变量的所有更改都可以通过lambda表达式看到。