C# 何时评估闭包中的属性?

C# 何时评估闭包中的属性?,c#,lambda,closures,C#,Lambda,Closures,我们的代码库中有几个方法使用“MaybeObject”,当结果可能已知或可能依赖尚未执行的外部webservice调用时,可以将其传递到函数中。例如,下面的属性可以具有指定的已知值,或者如果在异步调用完成后未指定并调用,则它将返回异步调用的结果 private string _internalString; public string stringProp { get { if (!string.IsNullOrEmpty(_internalString))

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

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

    set { _internalString = value; }
}
显然,在异步调用完成之前尝试引用该属性会导致空引用异常,因此我们也有一个标志来检查该值是否可用

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

在C#中,由于C#lambda闭包不是一个真正的闭包,它无法在创建时解决捕获环境的状态,因此在执行时将对所有闭包进行评估

() => doSomethingWithString(maybe.stringProp);
这里甚至有一个引用
可能
可能为空,在执行委托之前,您不会遇到任何类似于NullReferenceException的问题。此技巧有时用于后期绑定值解析

:

闭包在创建时保留对环境的引用 已创建(例如,本地变量的当前值 而泛型匿名函数不需要这样做 这个

C#闭包细节的良好概述-

从闭包的定义可以推断,闭包 在创建过程中记住变量的值。然而在 C#外部局部变量(在本例中为i)与 通过在堆上创建匿名方法。这意味着对 它在匿名方法中更改原始值以及 方法在第二次将修改后的i值设置为1时调用 (参见第二行输出)。这导致许多人认为匿名 方法实际上不是一个闭包,因为它的定义是 应记住创建闭包时的变量 不可修改


将推迟评估,直到调用结果操作。

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

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

显式方法(.NET 1.1):

private MaybeObject maybe;

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

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}
public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}
public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
匿名方法(.NET 2.0):

private MaybeObject maybe;

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

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}
public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}
public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
Lambda(.NET 3.5):

private MaybeObject maybe;

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

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}
public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}
public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
另请参见:

private MaybeObject maybe;

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

private void DoSomethingWithMaybeObject()
{
    doSomethingWithString(maybe.stringProp)
}
public Action ExampleMethod(MaybeObject maybe)
{
    return delegate() { doSomethingWithString(maybe.stringProp) };
}
public Action ExampleMethod(MaybeObject maybe)
{
    return () => doSomethingWithString(maybe.stringProp);
}
    • 这将是不同的。
      ExampleMethod()将委托返回给匿名函数()=>doSomethingWithString(可能是.stringProp),因此在调用该委托之前,不会调用doSomethingWithString()。您应该自己尝试一下,看看会发生什么

      在本例中,闭包似乎实际上围绕着
      MaybeObject
      ,而不仅仅是string属性(因为对象被传递给动作)。在执行操作之前,不会访问string属性

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

      string s = "";
      Func<bool> isGood = () => s == "Good"; 
      
      s = "Good";
      Console.WriteLine(isGood());
      
      // prints 'True'
      
      string s=”“;
      Func isGood=()=>s==“Good”;
      s=“好”;
      Console.WriteLine(isGood());
      //打印“真实”
      
      您可以在这里看到,闭包是围绕字符串生成的,但直到执行时才对其求值


      委托的工作方式与任何其他函数一样—在调用它们之前,它们实际上不会执行任何操作。

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

      我的猜测是,您无法确定,并且在规范中这两种情况都是有效的。让我们看看完整的答案是什么。+1“不是真正的终结”-有趣的一点,以前从未这样想过。