我对实际的 .NET 实现及其背后的决定感到好奇。
例如,在 Java 中,匿名类中使用的所有捕获值都必须是最终值。.NET 中似乎取消了此要求。
此外,与引用类型相比,值类型的捕获值的实现是否存在差异?
谢谢
找出它是如何实现的最简单的方法是尝试它。编写一些使用捕获变量的代码,编译它,然后在Reflector中查看它。请注意,捕获的是变量,而不是值。这是 Java 和 C# 在该领域的最大区别之一。
基本思想是,包含至少一个捕获变量的每一级范围会产生一个新类,其中包含已捕获变量的字段。如果有多个级别,则内部范围也有一个用于下一个范围的字段,依此类推。堆栈上真正的局部变量最终是对自动生成类的实例的引用。
这是一个例子:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
int copyOfI = i;
for (int j=0; j < 5; j++)
{
int copyOfJ = j;
actions.Add(delegate
{
Console.WriteLine("{0} {1}", copyOfI, copyOfJ);
});
}
}
foreach (Action action in actions)
{
action();
}
}
}
(当然,如果你不复制,你会得到不同的结果 - 实验!)这被编译成这样的代码:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
OuterScope outer = new OuterScope();
outer.copyOfI = i;
for (int j=0; j < 5; j++)
{
InnerScope inner = new InnerScope();
inner.outer = outer;
inner.copyOfJ = j;
actions.Add(inner.Action);
}
}
foreach (Action action in actions)
{
action();
}
}
class OuterScope
{
public int copyOfI;
}
class InnerScope
{
public int copyOfJ;
public OuterScope outer;
public void Action()
{
Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ);
}
}
}
对捕获变量的每个引用最终都会通过生成类的实例,因此它不仅仅是一次性副本。(好的,在这种情况下,代码中没有其他任何东西使用捕获的变量,但您可以轻松想象它可以。)注意对于外部循环的任何一次迭代,五个新实例都共享一个OuterScope
. 您可能想尝试在委托中使用额外的代码以查看它如何影响事物 - 如果委托更改,则copyofI
该更改将在下一个委托中看到;不会看到更改,copyOfJ
因为下一个委托将使用InnerScope
.