views:

94

answers:

3

I wanted a way to call a method asynchronously and have a function be fired upon completion. This is partially inspired by AJAX calls in web applications. Is my implementation ok? Is there a better way to do this?

public static class Tools
{

    public static void RunAsync(Action function, Action callback)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
        {
            callback();
        };
        worker.DoWork += delegate(object sender, DoWorkEventArgs e)
        {
            function();
        };
        worker.RunWorkerAsync();
    }

}

Is there a good way to extend this to work with functions that take arguments and have return values?

Thanks!

A: 

DoWorkEventArgs has an Argument member of type object that you can use to pass in arguments to your functions.

You can pass a data structure in e this way and use it to contain your parameters, and even your "return" values if you like. Do this by passing your state object into RunWorkerAsync(), e.g:

SomeCustomStateObject obj = new SomeCustomStateObject("foo", 1);
RunAsync(obj, func, callback);


// ...


public static void RunAsync(object target, Action function, Action callback)
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
    {
        callback();
    };

    worker.DoWork += delegate(object sender, DoWorkEventArgs e)
    {
        function(e);
    };



    worker.RunWorkerAsync(obj);
}

function would look something like this:

void function(DoWorkEventArgs e)
{
    SomeCustomStateObject obj = (SomeCustomStateObject)e.Argument;

    // ... do some stuff with obj
    // ... maybe put our "return" values back into obj
}
jscharf
A: 

How about changing that Action delegate to Action<T>.

public static class Tools 
{ 

    public static void RunAsync<T>(Action<T> function, Action callback, T parameter) 
    { 
        BackgroundWorker worker = new BackgroundWorker(); 
        worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) 
        { 
            callback(); 
        }; 
        worker.DoWork += delegate(object sender, DoWorkEventArgs e) 
        { 
            function(parameter); 
        }; 
        worker.RunWorkerAsync(); 
    } 

} 
Brian Gideon
A: 

Thanks Brian and jscharf for the answers! I constructed a set of functions using generics as suggested.

Below is one of them that takes two arguments and and has a return value:

    public static void RunAsync<T1, T2, TResult>(Func<T1, T2, TResult> function, T1 param1, T2 param2, Action<TResult> callback)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate(object sender, DoWorkEventArgs e)
        {
            e.Result = function(param1, param2);
        };
        worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
        {
            TResult result = (TResult)e.Result;
            callback(result);
        };
        worker.RunWorkerAsync();
    }

You could use it to do something like this (this was my goal):

        Tools.RunAsync<string, string>(Tools.ComputeSHA1Hash, filePath,
            delegate(string computedHash)
            {
                // do some processing with the hash
            }
        );
Venkat D.