什么时候关闭属性评估?

时间:2011-11-28 11:32:41

标签: c# lambda closures

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

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

    set { _internalString = value; }
}

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

问题是,在下面的代码中,lambda的创建会尝试并评估stringProp(可能尚未填充),或者会推迟评估,直到调用生成的Action(将在之后)检查异步操作是否完成)?

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}

6 个答案:

答案 0 :(得分:6)

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

() => doSomethingWithString(maybe.stringProp);

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

Wikipedia: Closure

  

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

C#Closure细节的精彩概述 - Anonymous methods and closures in C#

  

从Closure的定义可以推断出Closure   记住变量创建过程中的值。然而在   C#外部局部变量(在本例中为i)与之共享   通过在堆上创建它的匿名方法。这意味着任何改变   它在匿名方法中更改原始值并在何时更改   方法被称为第二次将i的修改值作为1   (见第二行输出)。这导致许多人认为匿名   方法实际上并不是一个Closure,因为它定义了a的值   应该记住在创建Closure时的变量   而不是可修改的。

答案 1 :(得分:3)

评估将推迟到调用生成的操作。

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

例如,传递代码以通过Action委托执行的这些方法都是等效的,并且在调用doSomethingWithString之前不会执行Action()方法:

显式方法(.NET 1.1):

private MaybeObject maybe;

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

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}

匿名方法(.NET 2.0):

public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}

Lambda(.NET 3.5):

public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}

另见:

答案 2 :(得分:2)

在运行操作之前,不会评估闭包中的属性,因为属性是方法,而不是值。访问MyObject.MyProperty就像调用代码MyObject.get_MyProperty()一样,因此在使用属性和使用底层变量时可以获得不同的行为。

答案 3 :(得分:0)

In .net closures are passed as reference so they are executed as they are evaluated, the same reason you can do something like this.


EventHandler handler = null;
handler = (sender, args) => { // event handler do something};
SomeEvent += handler;

答案 4 :(得分:0)

它会被贬低。 ExampleMethod()返回一个委托给匿名函数()=> doSomethingWithString(maybe.stringProp),因此在调用该委托之前不会调用doSomethingWithString()

答案 5 :(得分:0)

你应该亲自试试看看会发生什么!

在这种情况下,似乎闭包实际上是MaybeObject,而不仅仅是字符串属性(因为对象被传递给Action)。在执行Action之前,不会访问字符串属性。

但即使字符串本身是给闭包的变量......

string s = "";
Func<bool> isGood = () => s == "Good"; 

s = "Good";
Console.WriteLine(isGood());

// prints 'True'

您可以在此处看到闭包是围绕字符串进行的,但在执行之前不会对其进行求值。

代表的工作方式与任何其他功能一样 - 在调用之前,它们实际上并没有做任何事情。