c# - 为什么委托(delegate)实例不总是被缓存?

标签 c# .net delegates

为什么 C# 编译器不创建缓存 Action(SomeMethod) 的委托(delegate)实例的代码在这种情况下:

void MyMethod() {
   Decorator(SomeMethod);
}

void Decorator(Action a) { a(); }
void SomeMethod() { }

只有在 SomeMethod 时才会这样做是静态的:
static void SomeMethod() { }

编辑:

为了更清楚,让我们使用以下代码:
class A {
    public void M1() {
       var b = new B();
       b.Decorate(M2);
    }

    public void M2() {
    }
}


class B {
    public void Decorate(Action a) {
        Console.WriteLine("Calling");
        a();
    }
}


如果你想避免每次调用 M1 时分配委托(delegate),你可以很容易地做到这一点,但这很丑:
using System;

class A {
    Action _m2;

    public A() {
        _m2 = new Action(M2);
    }

    public void M1() {
       var b = new B();
       b.Decorate(_m2);
    }

     public void M2() {
     }
}


class B {
    public void Decorate(Action a) {
        Console.WriteLine("Calling");
        a();
    }
}

所以我的问题是,编译器无法生成类似代码的原因是什么?我看不到任何副作用。

我并不是说没有理由,从事编译器工作的人比我可能永远聪明得多。我只是想了解哪些情况不起作用。

最佳答案

它不能为实例方法缓存它,因为目标实例是委托(delegate)的一部分,并且它确实想使用静态字段进行缓存。不捕获任何变量等的静态方法调用可以非常便宜地缓存,但是当涉及状态时它会变得更加复杂,this算作状态。

是的,我想可以使用实例字段来缓存 () => this.SomeMethod() ,但坦率地说,this成为目标是一种相对罕见的情况,并不能解决一般问题。

但是,它也只对 lambda 语法执行此操作,即即使 SomeMethodstatic

Decorator(SomeMethod); // not cached
Decorator(() => SomeMethod()); // cached

可以看出区别here

这是因为差异是可检测的(不同的对象引用与相同的对象引用),并且理论上可能导致使用原始(非 lambda)语法的现有代码中的不同程序行为;因此,缓存条款迄今尚未追溯应用于旧语法。兼容性原因。不过,这已经讨论了多年。 IMO 这是其中之一,例如更改为 foreach L 值捕获,这可能会改变,而不会像我们想象的那样破坏世界。

要查看基于已编辑问题的示例中的理论差异:

using System;

class A
{
    static void Main()
    {
        var obj = new A();
        Console.WriteLine("With cache...");
        for (int i = 0; i < 5; i++) obj.WithCache();
        Console.WriteLine("And without cache...");
        for (int i = 0; i < 5; i++) obj.WithoutCache();
    }
    Action _m2;
    B b = new B();
    public void WithCache() => b.Decorate(_m2 ??= M2);
    public void WithoutCache() => b.Decorate(M2);
    public void M2() => Console.WriteLine("I'm M2");
}

 class B
{
    private object _last;
    public void Decorate(Action a)
    {
        if (_last != (object)a)
        {
            a();
            _last = a;
        }
        else
        {
            Console.WriteLine("No do-overs!");
        }
    }
}

这当前输出:
With cache...
I'm M2
No do-overs!
No do-overs!
No do-overs!
No do-overs!
And without cache...
I'm M2
I'm M2
I'm M2
I'm M2
I'm M2

关于c# - 为什么委托(delegate)实例不总是被缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60269336/

相关文章:

c# - 如何使 Flash ActiveX 控件在部署机器上兼容?

.net - 试图从两个 List<int> 对象中获取不同的值

尝试使用委托(delegate)时 Swift EXC_BAD_INSTRUCTION

c# - C# 中的 Rijndael key 大小

c# - 如何使用字节中的位

c# - 如何在母版页和内容(子)页中包含(引用)CSS Javascript jQuery

c# - C# using 语句自动处理对象的替代语法?

C#: Action 委托(delegate)与显式委托(delegate)

c# - 我应该使用什么模式将 .NET UI 耦合到将提供回调更新的 .NET 库类

c# - 如何从此字符串中删除转义字符?