Roy Osherove

View Original

The 3 ways to create a Thread-Safe GUI with .NET 2.0, with one clear winner.

With .NET 2.0 and with the new "anonymous delegates" feature in C#, doing multi threaded responsive GUIs is now easier. In fact, you get multiple choices on how to accomplish this task. But which choice is the best? and what does "best" really mean? more elegant? faster? more readable? all of the above?

Here's a little scenario for you. Assume there is a method that displays a string on the GUI, but may be called by a different thread than the GUI. The method has only a single string parameter. here are three versions of how you can implement this method and keep it thread safe. The last one is my favorite.

First, I'll declare two "generic" delegates. Each one is used in a different version of the method.

  delegate void  Func<T>(T t) ;
private void  displayError( string  message)
        {
            
if  (InvokeRequired)
            {
                Func<
string > del  displayError ;
                
Invoke(del, message) ;
                return;
            
}
            errorDisplay.Show(message)
;
        
}
This version uses a generic delegate which takes a string argument, then if Invoke is needed, it "Invokes" the delegate passing in the string argument. Note that passing in the string is not a strongly typed operation and may result in runtime errors if we make a mistake in the parameter order for example. That's why I don't like this version at all. But it's the most commonly used that I've seen, which is why I'm writing this.


delegate void  Func() ;
         private void  displayError1( string  message)
        {
            
if  (InvokeRequired)
            {
                Func del 
= delegate  { displayError1(message) } ;
                
Invoke(del) ;
                return;
            
}
            errorDisplay.Show(message)
;
        
}
        
This version uses a delegate with no parameters, and uses the "delegate" directive to create a delegate that calls the same method with the "message" parameter. Notice how lovely it is to be able to use a local scoped variable such as "message" in out delegate. It makes life much simpler because now all we ever need to do is just use one delegate signature for all of our thread-safe GUI work. This version is much better than the previous one because it's type-safe. We can't make a mistake in parameter order or type, or we'll have a build error. But this version is still not as elegant as I would like.


         private void  displayError2( string  message)
        {
            Func del 
= delegate
                
{
                    errorDisplay.Show(message)
;
                
} ;
            
Invoke(del) ;
        
}
This version uses the delegate directive to actually create the full functionality of our method. Notice that we *always* invoke the code in here using the "Invoke" method which is thread-safe, even if this is not required. Also note how much less code we have to write here, and that we always use the same delegate signature to accomplish this. Not only that, in contrast to the other two implementations, we only write the calling code once inside the delegate, and not another time outside of it which can lead to maintenance problem due to duplication. This method, to me, is the most elegant and readable. I don't really care about one extra call to the "Invoke" method, since this method is GUI related, and obviously is called after a long operation which was async. 10 nano seconds more or less won't matter one bit. This is the method I'd encourage you to use, even though it seems a bit un-orthodox at first, it is clearly the simplest of the three, with less code, less duplication and more readability.


Colorized by: CarlosAg.CodeColorizer

Unfortunately, all three methods require you to use C#, since anonymous delegates are not present in VB.NET yet.

Thoughts?