Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegates, Actions and Memory Allocations

Tags:

c#

delegates

I'm currently working on a chunk of code that requires minimum memory allocations.

I've noticed if I use a method for a parameter the compiler changes the code and creates a new Action, but if I use an anonymous method the compiler creates a delegate block. I'm aware that the new Action allocates memory, but I'm not so sure about the delegate. Will delegates allocate memory when used?

My code:

bool workfinished = false;

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

void MyMethod(Action callback1, Action callback2)
{
}

void CallBack1Work()
{
}

Compiler version:

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;
}
like image 205
TimD Avatar asked Dec 03 '13 17:12

TimD


People also ask

What is the difference between action and delegate?

The only difference between Action Delegates and Function Delegates is that Action Delegates does not return anything i.e. having void return type.

Which function do you use to delegate memory?

using System; delegate int MyDelegate(int a,int b);

What are the special features of delegate?

Delegates allow methods to be passed as parameters. Delegates are type safe function pointer. Delegate instances attach or detach a method at run time making it more dynamic and flexible to use. Delegates can invoke more than one method using the Multicast feature.

What are the types of delegate?

There are two types of delegates, singlecast delegates, and multiplecast delegates. Singlecast delegate point to single method at a time. In this the delegate is assigned to a single method at a time. They are derived from System.


2 Answers

If you use a lambda expression which doesn't capture anything, the compiler will generate a static field to cache it in. So use that, you can change the Action to an Action<YourClass> and call it with this. So:

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()
    {
       // ...
    }
}

That will only create delegate instances the first time DoWork is called on any instance. The delegates will then be cached for all future calls on all instances.

Admittedly this is all an implementation detail. You could always make it clearer:

class YourClass
{
    private static readonly Action<YourClass> Callback1 = x => x.Callback1Work();
    private static readonly Action<YourClass> Callback2 = x => x.workFinished = false;

    private bool workFinished;

    public void DoWork()
    {
        MyMethod(Callback1, Callback2);
    }

    ... code as before ...
}

It's worth actually profiling and benchmarking the code before you go to any of these lengths though.

Another alternative would be to stick to Action, but create instance variables for the delegates - so as long as you called DoWork multiple times on the same instance, you'd be okay:

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()
    {
        ...
    }
}
like image 129
Jon Skeet Avatar answered Oct 25 '22 04:10

Jon Skeet


It doesn't matter whether you explicit use new SomeDelegate or omit it, whether you use a lambda, the delegate keyword, or pass in a method group, or any possible solution you haven't shown. In every single case a delegate object will be created. The compiler can often infer that it should be there, so it doesn't force you to type it out; but the creation of that delegate is still happening no matter what. (Well, technically you could pass in null and not allocate an object, but then you can't ever do any work, so I think it's safe to ignore that case.)

The only real difference in memory allocations between each of the options is that in the given anonymous method blocks you are closing over a variable (workfinished). In order to create that closure the runtime will generate it's own type to store the state of the closure, create an instance of that type, and use that for the delegate, so all of the solutions using an anonymous method are creating one new object. (Granted, it's small, so it's not going to be particularly expensive in most situations.)

like image 43
Servy Avatar answered Oct 25 '22 04:10

Servy