Typesafe fire-and-forget asynchronous delegate invocation in C#
.net-4.0, asynchronous, c#, delegates, generics
Solution
You can pass EndInvoke as AsyncCallback for BeginInvoke:
Action<byte[], int, int> action = // ...
action.BeginInvoke(buffer, 0, buffer.Length, action.EndInvoke, null);
Does that help?
Problem
I recently found myself needing a typesafe "fire-and-forget" mechanism for running code asynchronously. Ideally, what I would want to do is something like: ``` var myAction = (Action)(() => Console.WriteLine("yada yada")); myAction.FireAndForget(); // async invocation ``` Unfortunately, the obvious choice of calling `BeginInvoke()` without a corresponding `EndInvoke()` does not work - it results in a slow resource leak (since the asyn state is held by the runtime and never released ... it's expecting an eventual call to `EndInvoke()`. I also can't run the code on the .NET thread pool because it may take a very long time to complete (it's advised to only run relatively short-lived code on the thread pool) - this makes it impossible to use the `ThreadPool.QueueUserWorkItem()`. Initially, I only needed this behavior for methods whose signature matches `Action`, `Action<...>`, or `Func<...>`. So I put together a set of extension methods (see listing below) that let me do this without running into the resource leak. There are overloads for each version of Action/Func. Unfortunately, I now want to port this code to .NET 4 where the number of generic parameters on Action and Func have been increased substantially. Before I write a T4 script to generate these, I was also hoping to find a simpler more elegant way to do this. Any ideas are welcome. ``` public static class AsyncExt { public static void FireAndForget( this Action action ) { action.BeginInvoke(OnActionCompleted, action); } public static void FireAndForget<T1>( this Action<T1> action, T1 arg1 ) { action.BeginInvoke(arg1, OnActionCompleted<T1>, action); } public static void FireAndForget<T1,T2>( this Action<T1,T2> action, T1 arg1, T2 arg2 ) { action.BeginInvoke(arg1, arg2, OnActionCompleted<T1, T2>, action); } public static void FireAndForget<TResult>(this Func<TResult> func, TResult arg1) { func.BeginInvoke(OnFuncCompleted<TResult>, func); } public static void FireAndForget<T1,TResult>(this Func<T1, TResult> action, T1 arg1) { action.BeginInvoke(arg1, OnFuncCompleted<T1,TResult>, action); } // more overloads of FireAndForget<..>() for Action<..> and Func<..> private static void OnActionCompleted( IAsyncResult result ) { var action = (Action)result.AsyncState; action.EndInvoke(result); } private static void OnActionCompleted<T1>( IAsyncResult result ) { var action = (Action<T1>)result.AsyncState; action.EndInvoke( result ); } private static void OnActionCompleted<T1,T2>(IAsyncResult result) { var action = (Action<T1,T2>)result.AsyncState; action.EndInvoke(result); } private static void OnFuncCompleted<TResult>( IAsyncResult result ) { var func = (Func<TResult>)result.AsyncState; func.EndInvoke( result ); } private static void OnFuncCompleted<T1,TResult>(IAsyncResult result) { var func = (Func<T1, TResult>)result.AsyncState; func.EndInvoke(result); } // more overloads of OnActionCompleted<> and OnFuncCompleted<> } ```