Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is async partial void MyPartialMethod() dangerous?

I've seen a lot of warnings about writing code such as...

public async void MyDangerousMethodWhichCouldCrashMyApp...

I've read that its OK with Eventhandlers as they must return void. However partial methods must also return void. You can have the following code...

static void Main(string[] args)
{
    MainAsync().Wait();
    Console.ReadLine();
}

async static Task MainAsync()
{
    MyCodeGeneratedClass c = new MyCodeGeneratedClass();
    try
    {
        await c.MyCodeGeneratedMethod();
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

public partial class MyCodeGeneratedClass
{
    public async Task MyCodeGeneratedMethod()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        MyCustomCode();
    }

    partial void MyCustomCode();
}

and then implement as...

partial class MyCodeGeneratedClass
{
    async partial void MyCustomCode()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        throw new Exception("Boom");
    }
}

But what will happen to the application if the implementation of MyCustomCode encounters an exception?

If it's a no go, given how prevalent async / await is, does that mean partial methods are essentially obsolete? Should code generation systems stop exposing partial methods in favour of events or perhaps better still empty protected virtual methods in a base class? i.e.

protected virtual Task MyCustomCode(T foo)
{
    return Task.FromResult(0);
}

EDIT: OK so I've made several updates to the code. After the original pseudo code I wrote didn't get well received. The code above I think demonstrates that there's definitely an issue with async partial void MyPartialMethod as the call to MyCodeGeneratedMethod does seem to bring down the app domain, despite the try catch around the call. I'm just wondering if there are better options than moving to protected virtual base class methods.

like image 426
Mick Avatar asked Sep 07 '16 05:09

Mick


People also ask

Why you shouldn't use async void?

Async void methods can wreak havoc if the caller isn't expecting them to be async. When the return type is Task, the caller knows it's dealing with a future operation; when the return type is void, the caller might assume the method is complete by the time it returns.

Can we return void in async method?

In short, if your async method is an event handler or a callback, it's ok to return void .

How does async void work?

async void has the same semantics as async Task , except for exceptions. An async void method will capture the current SynchronizationContext at the beginning of the method, and any exceptions from that method will be captured and raised directly on that captured context.

What is the difference between async void and async task?

A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run. A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done.


2 Answers

But what will happen to the application if the implementation of MyCustomCode encounters an exception?

The semantics for async void methods are that exceptions are raised directly on the SynchronizationContext that was current at the beginning of the method. This and other fun facts about async void are covered in my async best practices article.

does that mean partial methods are essentially obsolete?

Equally as obsolete as event handlers. So, no, not really. However, they weren't updated to allow a return type of Task, so it seems like they're a language feature that isn't being actively updated with the rest of the language.

Should code generation systems stop exposing partial methods in favour of events or perhaps better still empty protected virtual methods in a base class?

Events wouldn't change this at all; they'd still be implemented with async void.

If a "hook" needs to be asynchronous, then that changes all the generated code, since it must also all be asynchronous.

An async partial method would only not work if the generated code needed some result from that partial method and the implementation had to do some asynchronous work in order to generate that result. In my experience, partial methods are conceptually more like events, so async void would be acceptable.

like image 170
Stephen Cleary Avatar answered Oct 14 '22 17:10

Stephen Cleary


This seems to be a big issue for people working with Xamarin which generates code with a lot of hooks implemented as partial methods as can be seen in this thread...

https://forums.xamarin.com/discussion/2766/cannot-await-in-an-async-partial-method

As I posted there, I would recommend to anyone that is thinking about writing async code within a partial method to wrap their async code within a try catch as follows...

public partial class MyCodeGeneratedClass
{
    public async Task MyCodeGeneratedMethod()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));
        MyCustomCode();
    }

    partial void MyCustomCode();
}

partial class MyCodeGeneratedClass
{
    async partial void MyCustomCode()
    {
        try
        {
            await MyCustomCodeAsync();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    protected async Task MyCustomCodeAsync()
    {
        HttpClient client = new HttpClient();
        Console.WriteLine(await client.GetStringAsync("http://msdn.microsoft.com"));

        throw new Exception("Boom");
    }
}

Trying and catching around the async Task method should protect the app domain. Also, if/when Microsoft improve partial methods it might also make it easier to refactor your code.

As I have control over the scripts which generate the code in my project, I've chosen not to expose partial methods in situations where it's likely there'll be I/O and async is desirable and instead provide empty protected virtual Task methods in the base class. Also the generated code needs to be able to await the CustomCode method, there seems to be no way of doing that with async partial methods.

like image 23
Mick Avatar answered Oct 14 '22 16:10

Mick