Breaking News

Thursday, February 17, 2011

Delegates vs Methods

You might get into arguments with your peers upon using delegates over method calls.

Delegates have an entirely different purpose in their life, compared to a method call. If you can call a method directly, then you don't need a delegate. Because delegates are mostly useful when a function needs to be invoked in a context where the actual object is not available, such as event callbacks.

Or delegates are used in cases where you want a class to notify you, for instance, upon certain action.

Here is a scenario where you definitely need a delegate.

There is a sample worker class, that does some processing. While processing we require the class to send the updated status of the process.

//Small enumerated work statuses
enum WorkState
{
    Started,
    InProcess,
    AlmostThere,
    Completed
}


//A sample worker class, that uses the 
class Worker
{
    public void SomeWork(TheDelegate callback)
    {
        callback(this, WorkState.Started);

        int count = 0;
        while (count < 100)
        {
            count++;//the usual while counter.
            if(count>80)
                callback(this, WorkState.AlmostThere);
            else
                callback(this, WorkState.InProcess);
        }

        callback(this, WorkState.Completed);
    }
}



How to call the delegate?

//The delegate, that "delegates" the reference to the 
//actual callback method.
delegate void TheDelegate(Worker t, WorkState status);

class Program
{
    static void Main(string[] args)
    {
        Worker t = new Worker();

        //Pass on the reference to the callback method.
        t.SomeWork(new TheDelegate(MyCallbackMethod));

        Console.ReadKey();
    }

    //The call back method.
    static void MyCallbackMethod(Worker t, WorkState status)
    {
        Console.WriteLine("The task status is {0}", status);

        //Can also send it to a different network/pc or write into a file.
        //if (Network.IsConnectedToNetwork)
        //{
        //    Network.Send(IPAddress, t, status);
        //}
    }
}

Quite an interesting part to note that you can send the data anywhere you want, over intranet, internet, shared device, print server, etc.

Now you may question about the performance of delegate over method calls? Just so you may know there is not much of a difference: Performance of calling delegates vs methods?

Since CLR v2, the cost of delegate invocation is very close to that of virtual method invocation, which is used for interface methods.

Jon Skeet has provided his results:

Now I don't have particular faith that that means delegates are really faster than interfaces... but it makes me fairly convinced that they're not an order of magnitude slower. Additionally, this is doing almost nothing within the delegate/interface method. Obviously the invocation cost is going to make less and less difference as you do more and more work per call.

Joel Pobar's blog has some interesting facts about the cost of delegate invocation; it says its somehwat similar to virtual method call invocation for interface methods - or - should I say, our good old pure virtual functions - as we call it.

Enjoy! (0:

3 comments:

  1. This might be an old post (I can't see a date), but perhaps you can help sort out something.

    I'm using a delegate to update a TextBox from another thread, but that TextBox is multiline so I have a short sub to scroll it to the bottom whenever I add a line. I'm trying to figure out how to do a Delegate for this as well but I'm stumped. Can you help? Is there a better option?

    Private Sub scrollTextDel()
    txt_folderactivity.SelectionLength = 0
    txt_folderactivity.SelectionStart = txt_folderactivity.Text.Length
    txt_folderactivity.ScrollToCaret()
    End Sub

    I know this is VB and your article is C#, but I can translate if I need to. I just need some help getting going.

    ReplyDelete
  2. And gosh, after I sit here and think about your code, I tried...

    Private Sub SetText([text] As String)
    ' InvokeRequired required compares the thread ID of the
    ' calling thread to the thread ID of the creating thread.
    ' If these threads are different, it returns true and fires, Else not.
    Try
    If Me.txt_folderactivity.InvokeRequired Then
    Me.txt_folderactivity.Invoke(New SetTextCallback(AddressOf SetText), [text])
    Else
    Me.txt_folderactivity.AppendText([text])
    Me.txt_folderactivity.SelectionStart = txt_folderactivity.Text.Length
    Me.txt_folderactivity.ScrollToCaret()
    End If
    Catch ex As Exception
    Me.txt_folderactivity.AppendText(ex.Message & vbCrLf)
    End Try
    End Sub

    ...and I don't know exactly why it works but it does. So, you helped me by getting me to think about this from a different direction! Thank you!

    ReplyDelete
  3. Or... you can turn off `CheckForIllegalCrossThreadCalls=False` of your form.

    ReplyDelete

Designed By Published.. Blogger Templates