C# · 12月 28, 2021

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

由于我不完全是编程语言的专家,我很清楚这可能是一个愚蠢的问题,但是尽可能地告诉C#通过将匿名方法和闭包作为匿名嵌套类的实例方法来处理匿名方法和闭包[1],实例化这个类,然后将代理指向那些实例方法.

看来,这个匿名类只能被实例化一次(或者我错了吗?),那么为什么不让匿名类是静态的呢?

[1]实际上,它似乎有一个闭包类,一个匿名方法,没有捕获任何变量,我不完全理解的理由.

解决方法

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#你会更好一些.它根本没有关闭类.它使匿名函数成为当前类的静态函数.

是的,你错了.如果您只能分配一次代表,您可以离开,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> theFunction; static int Anonymous(int x,int y) { return x + y; } Func<int,int> M() { if (C1.theFunction == null) C1.theFunction = C1.Anonymous; return C1.theFunction; }}

不需要新课程

现在考虑一下

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

你知道为什么这不能用静态函数生成?静态函数需要访问this.x,但这是静态函数中的哪一个?没有一个

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

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

此外,我们不能再将代理缓存在静态字段中;你明白为什么吗

练习:代理可以缓存在实例字段中吗?如果没有,那么什么阻止这样做是合法的?如果是的话,反对实施这个“优化”的一些论据是什么?

现在考虑一下

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 + 0var d = new C3();var e = d.M(456);var f = e(); // 456 + 1var g = a.M(789);var h = g(); // 789 + 0

现在,代理人不仅需要知道this.x的值,还需要知道传入的y的值.必须存储在某个地方,所以我们将它存储在一个字段中.但是它不能是C3的一个领域,因为那么我们如何告诉b使用123和g来使用789的值呢?它们具有相同的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 x y,另一个是(int newY)=> {y = newY; }.描述两个代表的代码.

练习:现在假设M(int y)返回类型Func< int,Func< int,int>>我们返回一个=> b => this.x y z a b.描述代码.

练习:假设在这个和本地上关闭的lambda都是基本的非虚拟调用.出于安全考虑,从而不直接在虚拟方法的类型层次结构中的类型的代码进行基本调用是非法的.在这种情况下,描述如何生成可验证的代码.

练习:把所有的一起放在一起对于所有本地人,您如何使用getter和setter lambdas为多个嵌套的lambdas执行codegen?在基类调用的类和方法范围内通过泛型类型进行参数化?因为这是我们实际需要解决的问题.