我们的代码库中的几个方法使用'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);
}
答案 0 :(得分:6)
在C#中,所有将在执行时进行评估,因为C#lambda Closures不是真正的Closure,它能够在创建时解析捕获环境的状态。
() => doSomethingWithString(maybe.stringProp);
这里甚至引用maybe
可能为null,在执行委托之前你不会遇到像NullReferenceException这样的问题。这个技巧有时用于后期绑定值解析。
关闭时保留对环境的引用 已创建(例如,已创建为局部变量的当前值) 在封闭范围内)而通用匿名函数不需要这样做 此
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'
您可以在此处看到闭包是围绕字符串进行的,但在执行之前不会对其进行求值。
代表的工作方式与任何其他功能一样 - 在调用之前,它们实际上并没有做任何事情。