考虑此代码段并尝试猜测y1
和y2
评估为
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
实例的可见性?不知何故,当扩展方法与代表合并时,谁拥有什么信息(数据)变得越来越不清楚。
答案 0 :(得分:7)
AsDelegate
捕获变量value
(AsDelegate
的参数),而() => x
捕获变量x
。因此,如果更改x
的值,则lambda表达式将返回不同的值。但是,更改x
不会更改value
。
答案 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表达式看到。