C#代表,参考解析时间

我有一个关于.net委托的简单问题。 说我有这样的事情:

public void Invoke(Action action) { Invoke(() => action(this.Value)); } public void Invoke(Action action) { m_TaskQueue.Enqueue(action); } 

第一个函数包含对this.Value的引用。 在运行时,当第一个带有generics参数的方法被调用时,它会以某种方式提供this.Value到第二个,但是如何? 我想到了这些:

  • 按值调用(struct) – 传递this.Value的当前值,因此如果m_TaskQueue在5分钟后执行它,则该值将不会处于其最近状态,它将是第一次引用时的值。
  • 通过引用调用(引用类型) – 然后在执行操作期间引用最近的Value状态,但如果我在执行操作之前将this.Value更改为另一个引用,它仍将指向旧引用
  • 按名称调用(两者) – 在this.Value将评估this.Value 。 我相信实际的实现将持有this的引用,然后在实际执行委托期间评估该Value ,因为没有按名称调用。

我认为这将是名称样式的调用,但无法找到任何文档,所以想知道它是否是一个明确定义的行为。 这个类类似于Scala或Erlang中的Actor,所以我需要它是线程安全的。 我不希望Invoke函数立即取消引用Value ,这将通过m_TaskQueuethis对象的安全线程中m_TaskQueue

让我通过描述我们为此实际生成的代码来回答您的问题。 我将重命名你混淆的其他Invoke方法; 没有必要了解这里发生了什么。

假设你说

 class C { public T Value; public void Invoke(Action action) { Frob(() => action(this.Value)); } public void Frob(Action action) { // whatever } } 

编译器生成的代码就像您实际编写的一样:

 class C { public T Value; private class CLOSURE { public Action ACTION; public C THIS; public void METHOD() { this.ACTION(this.THIS.Value); } } public void Invoke(Action action) { CLOSURE closure = new CLOSURE(); closure.THIS = this; closure.ACTION = action; Frob(new Action(closure.METHOD)); } public void Frob(Action action) { // whatever } } 

这是否回答你的问题?

委托存储对变量的引用,而不是它的值。 如果你想保持当前值(假设它是一个值类型),你需要制作它的本地副本:

 public void Invoke(Action action) { var localValue = this.Value; Invoke(() => action(localValue)); } 

如果它是可变引用类型,则可以创建本地克隆/深层副本。

真正的关键是要记住范围是词汇; 这是编译器负责的事情。 所以它捕获变量 ,而不是它们的 。 这些值是值类型还是引用类型是另一回事。

也许更改代表行为的一个稍微更极端的例子将有助于:

 var myVariable = "something"; Action a = () => Console.WriteLine(myVariable); myVariable = "something else entirely" a(); 

打印“完全不同的东西”。 从这个角度来看,包装,保存或移动function的次数并不重要; 它仍然指的是它所包含的变量。 因此,简而言之,重要的是当委托实际执行时封闭变量的值。