C# 委托、操作和内存分配

C# 委托、操作和内存分配,c#,delegates,C#,Delegates,我目前正在处理一段需要最小内存分配的代码 我注意到,如果我对参数使用方法,编译器会更改代码并创建一个新的操作,但是如果我使用匿名方法,编译器会创建一个委托块。我知道新的操作分配内存,但我对委托不太确定。学员在使用时会分配内存吗 我的代码: bool workfinished = false; void DoWork() { MyMethod(CallBack1Work, ()=>{ workfinished = false;}); } void MyMethod(Action

我目前正在处理一段需要最小内存分配的代码

我注意到,如果我对参数使用方法,编译器会更改代码并创建一个新的
操作
,但是如果我使用匿名方法,编译器会创建一个委托块。我知道新的
操作
分配内存,但我对委托不太确定。学员在使用时会分配内存吗

我的代码:

bool workfinished = false;

void DoWork()
{
    MyMethod(CallBack1Work, ()=>{ workfinished = false;});
}

void MyMethod(Action callback1, Action callback2)
{
}

void CallBack1Work()
{
}
编译器版本:

bool workfinished = false;

void DoWork()
{
    MyMethod(new Action( CallBack1Work ), delegate{ workfinished = false;});
}

void MyMethod(Action callback1, Action callback2)
{
}

void CallBack1Work()
{
}
        
void DoWork_b01()
{
    workfinished = false;
}

无论是显式使用
newsomedelegate
还是省略它,无论是使用lambda、
delegate
关键字,还是传入方法组,或者任何可能的解决方案,都没有关系。在每种情况下,都将创建一个委托对象。编译器通常可以推断它应该在那里,因此它不会强迫您键入它;但无论如何,该委托的创建仍在进行。(从技术上讲,您可以传入
null
而不分配对象,但这样您就永远无法进行任何工作,因此我认为忽略这种情况是安全的。)


每个选项之间内存分配的唯一真正区别在于,在给定的匿名方法块中,您正在关闭一个变量(
workfinished
)。为了创建该闭包,运行时将生成自己的类型来存储闭包的状态,创建该类型的实例,并将其用于委托,因此所有使用匿名方法的解决方案都将创建一个新对象。(当然,它很小,所以在大多数情况下不会特别昂贵。)

如果使用不捕获任何内容的lambda表达式,编译器将生成一个静态字段来缓存它。因此,使用该选项,您可以将
操作
更改为
操作
,并使用
this
调用它。因此:

class YourClass
{
    private bool workFinished;

    public void DoWork()
    {
        MyMethod(instance => instance.Callback1Work(),
                 instance => instance.workFinished = false);
    }

    private void MyMethod(Action<YourClass> callback1,
                          Action<YourClass> callback2)
    {
        // Do whatever you want here...
        callback1(this);
        // And here...
        callback2(this);
    }

    private void Callback1Work()
    {
       // ...
    }
}
不过,在进行这些长度的测试之前,对代码进行评测和基准测试是值得的

另一种选择是坚持执行
Action
,但为代理创建实例变量-因此只要在同一实例上多次调用
DoWork
,您就可以:

class YourClass
{
    private readonly Action foo;
    private readonly Action bar;

    private bool workFinished;

    public YourClass()
    {
        foo = Callback1Work;
        bar = () => workFinished = false;
    }

    public void DoWork()
    {
        MyMethod(foo, bar);
    }

    public void MyMethod(Action callback1, Action callback2)
    {
        ...
    }

    private void Callback1Work()
    {
        ...
    }
}

看一看,也可以更改
MyMethod
以接受
操作
?这将启用更多缓存选项…这是一个选项,我不太关心内存量,我主要关心的是生成垃圾,比如如果调用DoWork方法一百万次,将分配多少内存。我必须运行测试,但听到专家这样说总是很好:)我也在尝试最小化分配。我已经注意到,
操作
是在Debug中分配的,而不是在Release中分配的。(我正在使用Visual Studio 2015,并在64位Windows上运行.NET 4.5.2。)值得注意的是,在某些情况下,编译器会缓存和重用lambda的单个委托实例,特别是在没有捕获上下文的情况下。谢谢,我假设delegate关键字实际上是在创建一个新的委托对象,但我不是100%确定。是的,令人困惑的是有一个方法正在创建,所以我不确定它是一次性分配还是什么。看起来有点像callback2_b01@TimD:每次呼叫分配一次。一个小的分配,但仍然存在。我已经提出了一个未显示的替代方案,它不会在每次调用中创建一个委托。。。
class YourClass
{
    private readonly Action foo;
    private readonly Action bar;

    private bool workFinished;

    public YourClass()
    {
        foo = Callback1Work;
        bar = () => workFinished = false;
    }

    public void DoWork()
    {
        MyMethod(foo, bar);
    }

    public void MyMethod(Action callback1, Action callback2)
    {
        ...
    }

    private void Callback1Work()
    {
        ...
    }
}