为什么C#将匿名方法和闭包实现为实例方法,而不是静态方法? [英] Why does C# implement anonymous methods and closures as instance methods, rather than as static methods?

查看:100
本文介绍了为什么C#将匿名方法和闭包实现为实例方法,而不是静态方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这个匿名类似乎只能实例化一次(或者我对此有错吗?),为什么不让匿名类成为静态对象呢?


[1]实际上,似乎有一个用于闭包的类和一个用于不捕获任何变量的匿名方法的类,我对此并不完全理解.

解决方案

我很清楚这可能是一个愚蠢的问题

不是.

C#通过将匿名方法和闭包处理为匿名嵌套类的实例方法,实例化该类,然后将委托指向这些实例方法,来处理这些匿名方法和闭包.

C#有时 这样做.

这个匿名类似乎只能实例化一次(或者我对此有错吗?),那么为什么不将匿名类设为静态呢?

在某些情况下,C#可以帮助您更好.它根本不构成闭包类.它使匿名函数成为当前类的静态函数.

是的,您对此有误.如果只分配一次委托就可以解决问题,那么C#确实可以解决这个问题.

(严格地说,这并非完全正确;在某些模糊的情况下,未实现此优化.但在大多数情况下是这样.)

实际上,似乎有一个用于闭包的类和一个用于不捕获任何变量的匿名方法的类,我对此并不完全理解.

您已经将手指放在您不太了解的地方.

让我们看一些示例:

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 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;
  }
}

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

锻炼:可以将委托缓存在实例字段中吗?如果否,那么是什么阻止了这种行为的合法性呢?如果是,那么反对实现这种优化"的一些理由是什么?

现在考虑:

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返回一个代表元组,一个代表为()=>x + y,另一个代表为(int newY)=>{ y = newY; }.描述两个委托的代码生成.

锻炼:现在假设M(int y)返回类型Func<int, Func<int, int>>,而我们返回a => b => this.x + y + z + a + b.描述代码源.

锻炼:假设同时关闭了this的lambda和本地进行的base非虚拟调用.出于安全原因,从类型内的代码而不是直接在虚拟方法的类型层次结构中进行base调用是非法的.描述在这种情况下如何生成可验证的代码.

锻炼:将它们放在一起.如何用base调用的类和方法范围中的泛型类型对所有嵌套的getter和setter lambda使用所有getter和setter lambda进行代码生成?因为那是我们实际上必须解决的问题.

As I'm not exactly an expert on programming languages I'm well aware this may be a stupid question, but as best as I can tell C# handles anonymous methods and closures by making them into instance methods of an anonymous nested class [1], instantiating this class, and then pointing delegates at those instance methods.

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?


[1] 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.

解决方案

I'm well aware this may be a stupid question

It's not.

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# does that sometimes.

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?

In cases where that would be legal, C# does you one better. It doesn't make a closure class at all. It makes the anonymous function a static function of the current class.

And yes you are wrong about that. In cases where you can get away with only allocating the delegate once, C# does get away with it.

(This is not strictly speaking entirely true; there are some obscure cases where this optimization is not implemented. But for the most part it is.)

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.

You have put your finger on the thing you don't adequately understand.

Let's look at some examples:

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

This can be generated as

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;
  }
}

No new class needed.

Now consider:

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

Do you see why this cannot be generated with a static function? The static function would need access to this.x but where is the this in a static function? There isn't one.

So this one has to be an instance function:

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

Also, we can no longer cache the delegate in a static field; do you see why?

Exercise: could the delegate be cached in an instance field? If no, then what prevents this from being legal? If yes, what are some arguments against implementing this "optimization"?

Now consider:

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

This cannot be generated as an instance function of C3; do you see why? We need to be able to say:

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

Now the delegates need to know not just the value of this.x but also the value of y that was passed in. That has to be stored somewhere, so we store it in a field. But it can't be a field of C3, because then how do we tell b to use 123 and g to use 789 for the value of y? They have the same instance of C3 but two different values for 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;
  }
}

Exercise: Now suppose we have C4<T> with a generic method M<U> where the lambda is closed over variables of types T and U. Describe the codegen that has to happen now.

Exercise: Now suppose we have M return a tuple of delegates, one being ()=>x + y and the other being (int newY)=>{ y = newY; }. Describe the codegen for the two delegates.

Exercise: Now suppose M(int y) returns type Func<int, Func<int, int>> and we return a => b => this.x + y + z + a + b. Describe the codegen.

Exercise: Suppose a lambda closed over both this and a local does a base non-virtual call. It is illegal to do a base call from code inside a type not directly in the type hierarchy of the virtual method, for security reasons. Describe how to generate verifiable code in this case.

Exercise: Put 'em all together. How do you do codegen for multiple nested lambdas with getter and setter lambdas for all locals, parameterized by generic types at the class and method scope, that do base calls? Because that's the problem that we actually had to solve.

这篇关于为什么C#将匿名方法和闭包实现为实例方法,而不是静态方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆