什么时候关闭属性评估?

mav*_*vnn 7 c# lambda closures

我们的代码库中的几个方法使用'MaybeObject',可以在结果可能已知时传递给函数,或者可能依赖于尚未执行的外部Web服务调用.例如,下面的属性可以具有指定的已知值,或者如果未指定并在异步调用完成后调用它将返回异步调用的结果.

private string _internalString;
public string stringProp
{ 
    get
    {
        if (!string.IsNullOrEmpty(_internalString))
            return _internalString;
        return resultOfAsyncCallFromSomewhereElse;
    }

    set { _internalString = value; }
}
Run Code Online (Sandbox Code Playgroud)

显然,尝试在异步调用完成之前引用该属性会导致空引用异常,因此我们还有一个标志来检查该值是否可用.

问题是,在下面的代码中会创建lambda尝试和评估stringProp(可能还没有填充),或者是否会推迟评估直到调用结果Action(在检查异步操作完成后)?

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
Run Code Online (Sandbox Code Playgroud)

sll*_*sll 6

在C#中,所有将在执行时进行评估,因为C#lambda Closures不是真正的Closure,它能够在创建时解析捕获环境的状态.

() => doSomethingWithString(maybe.stringProp);
Run Code Online (Sandbox Code Playgroud)

这里甚至引用maybe可能为null,在执行委托之前你不会遇到像NullReferenceException这样的问题.这个技巧有时用于后期绑定值解析.

维基百科:关闭:

闭包在创建时保留对环境的引用(例如,到封闭范围中的局部变量的当前值),而通用匿名函数不需要这样做.

C#Closure细节概述 - C#中的匿名方法和闭包

根据Closure的定义,可以推断Closure在创建过程中会记住变量的值.但是在C#中,通过在堆上创建外部局部变量(在本例中为i)与匿名方法共享.这意味着在匿名方法中对它的任何更改都会更改原始值,并且在第二次调用该方法时,它将i的修改值设置为1(请参阅第二行输出).这导致许多人认为匿名方法实际上不是Closure,因为它的定义应该记住在创建Closure时变量的值而不是可修改的.

  • +1'不是真正的封闭' - 有趣的一点,从来没有想过它. (2认同)

Enr*_*lio 3

评估将推迟到调用结果操作为止。

委托引用的代码仅在显式调用委托本身时执行,无论委托本身是如何创建的

例如,通过Action委托传递要执行的代码的这些方式都是等效的,并且doSomethingWithString只有在调用以下方法时才会执行该方法Action()

显式方法(.NET 1.1):

private MaybeObject maybe;

public Action ExampleMethod()
{
    return new Action(DoSomethingWithMaybeObject);
}

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}
Run Code Online (Sandbox Code Playgroud)

匿名方法(.NET 2.0):

public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}
Run Code Online (Sandbox Code Playgroud)

拉姆达(.NET 3.5):

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: