为什么不总是缓存委托实例?

唐盒

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

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时都分配委托,您可以轻松做到,但是非常难看:

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

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

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

马克·格雷韦尔

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

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

但是,它也只能做这行拉姆达语法,也就是说,即使SomeMethodstatic

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

你可以在这里看到区别

这是因为差异是可以检测到的(不同的对象ref与相同的对象ref),并且在理论上可能会导致使用原始(非lambda)语法的现有代码中的程序行为有所不同。因此,到目前为止,缓存规定尚未追溯应用到旧语法。兼容性原因。但是,已经讨论了很多年了。IMO是像更改foreachL值捕获这样的事情之一,可以在不像我们想象的那样破坏世界的情况下进行更改。


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

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章