Gerhard Schreurs
Gerhard Schreurs

Reputation: 663

Can I refactor code to fix "Asynchronous method should not return void"?

I am doing a project in c# using Xamarin. The compiler warns me that "Asynchronous method 'HandleWidget_ClickButton' should not return void". See example here:

//Code is simplified

public class Widget {
   public event Action<int> ClickButton;

   private void FireClickButton (int id)
   {
       if (ClickButton != null) {
           ClickButton (id);
       }
   }

   //somewhere else i call FireClickButton(1);
}

public class MyFragment {
    private _widget Widget;

    public override View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        //...
        _widget = view.FindViewById<Widget> (Resource.Id.widget);
        //...
    }


    public override void OnResume ()
    {
        base.OnResume ();
        _widget.ClickButton += HandleWidget_ClickButton;
    }

    async void HandleWidget_ClickButton (int id)
    {
        await SaveSomethingInStorage (id);
    }
}

Can I somehow return something to an event/action/delegate? I do not know if this is possible, or how to write this, syntax-wise, and I've spent quite some time searching for a solution. Elsewhere I read that when handling events, it's ok to use async void (instead of something like async Task), but I like to avoid warnings and do not like to hide them using #Pragma instructions.

EDIT (With answer) from @hvd:

You created an event handler, but you don't follow the .NET conventions for event handlers. You should. The warning is automatically suppressed for what can be detected as event handlers.

The .NET conventions for event handlers require a sender, of type object, and the event arguments, of type EventArgs or a class derived from EventArgs. If you use this signature, you should no longer get a warning.

I rewrote my code like this:

public class MyEventArgs : EventArgs
{
    public MyEventArgs (int id)
    {
        ID = id;
    }

    public int ID;
}

public class Widget {
    public event EventHandler<MyEventArgs> ClickTest;

    void FireClickButton (int id)
    {
        if (ClickTest != null) {
            ClickTest (this, new MyEventArgs (id));
        }
    }
}

//In observer class
_widget.ClickTest += HandleWidget_ClickTest;

async void HandleWidget_ClickTest (object sender, MyEventArgs e)
{
    await DoSomethingAsync (e.ID);
}

Note that you must derive from EventArgs. Doing it like this does not suppress the warning:

public event EventHandler<int> AnotherClickTest;

if (AnotherClickTest != null) {
   AnotherClickTest (this, 1);
}

Upvotes: 1

Views: 1012

Answers (3)

James Westgate
James Westgate

Reputation: 11444

As mentioned in other answers, you can structure your event handlers correctly so that this is not raised, as the runtime will know how to correctly call the delegate. Here is some code showing you how to structure this:

Expose a public Command to your view

public ICommand CopyDeviceId { get; private set; }

Then, create a command instance with a delegate

CopyDeviceId = new Command(() => CopyDeviceIdValue(this, EventArgs.Empty));

Finally, create the asyncronous delegate

private async void CopyDeviceIdValue(object sender, EventArgs e)
{
    \\code here
}

Upvotes: 0

user743382
user743382

Reputation:

You created an event handler, but you don't follow the .NET conventions for event handlers. You should. The warning is automatically suppressed for what can be detected as event handlers.

The .NET conventions for event handlers require a sender, of type object, and the event arguments, of type EventArgs or a class derived from EventArgs. If you use this signature, you should no longer get a warning.

Upvotes: 1

Vlad
Vlad

Reputation: 555

Async methods always should return a Task. A Task represents an action that has not finished yet. Afterwards you can do a Wait() on a task, to get the finished result... since your method is Void, you won't do any wait. The signature should look like this:

async Task HandleWidget_ClickButton (int id)

If you wanted to return something, it would be like this (for example, int):

async Task<int> HandleWidget_ClickButton (int id)

Upvotes: 1

Related Questions