Saturday, November 22, 2008

Action Delegate on the Compact Framework 3.5

I wanted to just raise awareness of the new Action delegate available on the Compact Framework 3.5. It saves the need to explicitly create delegates then assign methods or even using anonymous delegates as in CF 2.0. This shouldn't be confused with the generic Action delegate introduced in .NET 2.0.

I mentioned the Action delegate in a session I did at Tech Ed EMEA recently and it seems not many people are using it or are aware that it even exists, hopefully this post should change that.

In the bad old days of pre .NET CF 2.0 we had to write some code like the following:
public MyClass
{
private delegate void StorageCardRemoved(string message);
private StorageCardRemoved storageCardRemoved = null;

public MyClass()
{
storageCardRemoved =
new StorageCardRemoved(ShowStorageCardRemovedNotification);
}

private void ShowStorageCardRemovedNotification(string name)
{
MessageBox.Show(name);
}

//Event handler from some worker process running somewhere.
private void DeviceManagement_StorageCardRemoved(object sender,
StorageCardChangedEventArgs args)
{
Invoke(storageCardRemoved, args.Name);
}
}
The above senario is not uncommon for device developers. We are used to writing worker threads to "listen" or just do some work on a separate thread. At some point we need to tell the user that something happend.

The above code is long and complicated. This is where the Action delegate makes this simple. Consider the above refactored to the following:
public MyClass
{
//Event handler from some worker process running somewhere.
private void DeviceManagement_StorageCardRemoved(object sender,
StorageCardChangedEventArgs args)
{
Invoke(new Action(() => MessageBox.Show(args.Name)));
}
}
How much simpler is that. The only limitation with this is that you can't call anything passing parameters. So you couldn't for example delay the message box processing by calling another delegate as you wouldn't be able to pass it the name (as in this case). This is a limitation within the Invoke method not supporting a paramerterized Action delegate. The action delegate supports upto 4 generic parameters with no returning type. This is the same functionality as per the desktop.

Of course you only need to use Invoke and Action in the above example if the event was received on a thread other than the one that owns the controls underlying window handle (UI thread).

Take another scenario that uses the Action delegate. In this example it is a situation where you have some code to be executed, but each time it is executed you want to do some other stuff first. This initial stuff might need to be specified by the caller at runtime. Consider the following code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
var businessClass =
new BusinessClass(() => Debug.WriteLine("Do initialization stuff"));
businessClass.Process(() => Debug.WriteLine("Now do business process"));
}
}
public class BusinessClass
{
public BusinessClass(Action init)
{
Init = init;
}

public Action Init
{
internal get;
set;
}

public void Process(Action action)
{
Debug.WriteLine("Now entered Process");
Init();
action();
}
}
We have a form with a button event handler hooked up to a button. When we click the button the event handler is called. When we construct the BusinessClass, we pass in the constructor an initialization Action delegate that gets executed when the Process method is called and before the Process Action delegate gets executed.

If we run this code and click the button, the output is as follows:
Now entered Process
Do initialization stuff
Now do business process
As you can see, it is quite a powerful and easy implementation. Next I'll talk about the Func delegate....

No comments: