Migrating Delegate.BeginInvoke Calls to .NET Core, .NET 5 and .NET 6
source link: https://blog.ndepend.com/migrating-delegate-begininvoke-calls-to-net-core-net-5-and-net-6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Migrating Delegate.BeginInvoke Calls to .NET Core, .NET 5 and .NET 6
In this 2019 post, the .NET Base Class Library engineers announced that the good old Delegate.BeginInvoke
.NET Framework syntax wasn’t supported in .NET Core and consequently in .NET 5, 6 … The reasons for this are twofold:
- The Task-based Asynchronous Pattern (TAP) is the recommended async model as of .NET Framework 4.5.
- The implementation of async delegates depends on remoting features not present in .NET Core,
The detailed discussion about this decision can be found here https://github.com/dotnet/runtime/issues/16312.
In the same post a code sample is provided to achieve the Delegate.BeginInvoke
features with the TAP and the async/await
keywords. I don’t find this solution satisfying because it requires significant refactoring for porting some Delegate.BeginInvoke
calls to .NET Core.
Let’s see how we can implement the two primary Delegate.BeginInvoke
usage scenarios with .NET Standard code. The key is to keep a syntax similar enough to simplify the migration from .NET Fx to .NET Core/5/6.
Scenario 1: Call EndInvoke() and on IAsyncResult object returned by BeginInvoke()
Here is the syntax we can achieve with the code below:
Find below the whole implementation followed by unit tests that 100% test it. Here are some remarks:
- Ideally we would have wanted a syntax like
FuncDouble.MyBeginInvoke(11)
but there is no C# delegate type inference at call site. Thus we need to usenew Func<int, int>(FuncDouble).MyBeginInvoke(11)
instead. - A trivial
MyBeginInvoke()
overload is needed for eachFunc<T0,...,TN,Result>
cardinality N. - I’ve never been a fan of
Delegate.EndInvoke()
re-throwing an exception thrown while executing the asynchronous procedure. ThusIMyAsyncResult<TResult>
presents abool TryEndInvoke(out TResult result, out Exception exception)
method that returns false and the exception when failed. This discards the need for a try{ } catch{ } clause when callingEndInvoke()
. TryEndInvoke()
is called on theIMyAsyncResult
object and not on the delegate object as originally. This syntax is easier since only one object needs to be provided instead of two to conclude the async task.- The key of this implementation is the private nested class
MyAsyncResult<TResult>
that keeps a reference to the async task and waits for termination upon aTaskAwaiter
object. - Finally, notice that
TryEndInvoke()
can be called only once. AnInvalidOperationException
is thrown the second time it is called.
Here are the tests that challenge all possible paths. Thanks to some usage of Thread.CurrentThread.ManagedThreadId
these tests check that the asynchronous procedure is actually executed on a background thread.
Scenario 2: Call an On-Task-Completed-Action once the asynchronous call is terminated
The second usual scenario to achieve is to provide an On-Task-Completed-Action to consume the result – or eventually the exception thrown – instead of calling an EndInvoke()
method.
Here is the code to achieve that. The astute is to rely on Task.ContinueWith()
.
Here are the tests that fully cover this implementation.
Conclusion
Migrating .NET Fx calls to Delegate.BeginInvoke()
to .NET Core .NET 5/6 can be tricky. Hopefully the code provided in this post greatly simplifies this task.
My dad being an early programmer in the 70's, I have been fortunate to switch from playing with Lego, to program my own micro-games, when I was still a kid. Since then I never stop programming.
I graduated in Mathematics and Software engineering. After a decade of C++ programming and consultancy, I got interested in the brand new .NET platform in 2002. I had the chance to write the best-seller book (in French) on .NET and C#, published by O'Reilly (> 15.000 copies) and also did manage some academic and professional courses on the platform and C#.
Over the years, I gained a passion for understanding structure and evolution of large complex real-world applications, and for talking with talented developers behind it. As a consequence, I got interested in static code analysis and started the project NDepend.
Today, with more than 8.000 client companies, including many of the Fortune 500 ones, NDepend offers deeper insight and understanding about their code bases to a wide range of professional users around the world.
I live with my wife and our twin babies Léna and Paul, in the beautiful island of Mauritius in the Indian Ocean.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK