为什么 C# 将匿名方法和闭包实现为实例方法,而不是静态方法?

Why does C# implement anonymous methods and closures as instance methods, rather than as static methods?

由于我不是编程语言方面的专家,我很清楚这可能是一个愚蠢的问题,但据我所知,C# 通过将匿名方法和闭包变成匿名对象的实例方法来处理匿名方法和闭包嵌套 class [1],实例化此 class,然后将委托指向这些实例方法。

看来这个匿名 class 只能实例化一次(或者我对此有误吗?),那么为什么不让匿名 class 成为静态的呢?


[1] 实际上,看起来有一个 class 用于闭包,一个用于不捕获任何变量的匿名方法,我也不完全理解两者的基本原理。

I'm well aware this may be a stupid question

不是。

C# handles anonymous methods and closures by making them into instance methods of an anonymous nested class, instantiating this class, and then pointing delegates at those instance methods.

C# 有时

It appears that this anonymous class can only ever be instantiated once (or am I wrong about that?), so why not have the anonymous class be static instead?

在合法的情况下,C# 会更好。它根本不会关闭 class。它使匿名函数成为当前class.

的静态函数

是的,你错了。如果您只分配一次委托就可以逃脱,C# 确实 逃脱了它。

(严格来说这并不完全正确;在一些模糊的情况下未实现此优化。但在大多数情况下是这样。)

Actually, it looks like there's one class for closures and one for anonymous methods that don't capture any variables, which I don't entirely understand the rationale for either.

你把手指放在了你没有充分理解的东西上。

让我们看一些例子:

class C1
{
  Func<int, int, int> M()
  {
    return (x, y) => x + y;
  }
}

这可以生成为

class C1
{
  static Func<int, int, int> theFunction;
  static int Anonymous(int x, int y) { return x + y; }
  Func<int, int, int> M()
  {
    if (C1.theFunction == null) C1.theFunction = C1.Anonymous;
    return C1.theFunction;
  }
}

不需要新的 class。

现在考虑:

class C2
{
  static int counter = 0;
  int x = counter++;
  Func<int, int> M()
  {
    return y => this.x + y;
  }
}

你明白为什么这不能用静态函数生成吗? 静态函数需要访问 this.x 但静态函数中的 this 在哪里?一个都没有。

所以这个必须是一个实例函数:

class C2
{
  static int counter = 0;
  int x = counter++;
  int Anonymous(int y) { return this.x + y; }
  Func<int, int> M()
  {
    return this.Anonymous;
  }
}

此外,我们不能再将委托缓存在静态字段中;你知道为什么吗?

练习:委托可以缓存在实例字段中吗?如果不是,那么是什么阻止了这合法化?如果是,反对实施此 "optimization" 的一些论据是什么?

现在考虑:

class C3
{
  static int counter = 0;
  int x = counter++;
  Func<int> M(int y)
  {
    return () => x + y;
  }
}

这不能生成为C3的实例函数;你知道为什么吗?我们需要能够说:

var a = new C3();
var b = a.M(123);
var c = b(); // 123 + 0
var d = new C3();
var e = d.M(456);
var f = e(); // 456 + 1
var g = a.M(789);
var h = g(); // 789 + 0

现在委托人不仅需要知道 this.x 的值,还需要知道传入的 y 的值。必须将其存储在某处,所以我们将它存储在一个字段中。但它不能是C3的字段,因为那我们如何告诉b使用123和g使用789作为y的值?它们具有相同的 C3 实例,但 y.

具有两个不同的值
class C3
{
  class Locals
  {
    public C3 __this;
    public int __y;
    public int Anonymous() { return this.__this.x + this.__y; }
  }
  Func<int> M(int y)
  {
    var locals = new Locals();
    locals.__this = this;
    locals.__y = y;
    return locals.Anonymous;
  }
}

练习:现在假设我们有 C4<T> 和一个泛型方法 M<U>,其中 lambda 对类型 T 和 U 的变量封闭。描述现在必须发生的代码生成。

练习:现在假设我们有 M return 个委托元组,一个是 ()=>x + y,另一个是 (int newY)=>{ y = newY; }。描述两位代表的代码生成器。

练习:现在假设 M(int y) return 键入 Func<int, Func<int, int>> 并且我们 return a => b => this.x + y + z + a + b。描述代码生成器。

练习:假设 lambda 在 this 上关闭并且本地执行 base non-virtual 调用。出于安全原因,从不直接位于虚方法类型层次结构中的类型内部的代码执行 base 调用是非法的。描述在这种情况下如何生成可验证代码。

练习:把它们放在一起。您如何为所有局部变量使用 getter 和 setter lambda 对多个嵌套 lambda 进行代码生成,由 class 和方法范围内的泛型参数化,执行 base 调用?因为这就是我们实际必须解决的问题