Reputation: 989
I've got a class with some async methods that take a handler class in order to decide what to do on success or failure.
public class FooModel
{
private FooState fooState;
public async Task UpdateFooAsync(IHandler handler)
{
try
{
var newFooState = await Something();
fooState = newFooState;
handler.Success();
}
catch
{
handler.Error();
}
}
}
public interface IHandler
{
void Success();
void Error();
}
I might call UpdateFooAsync from several different places in my code. And depending on where I call it from, I might want to do different things. For example, a synchronous example:
public async void update_click(object sender, RoutedEventArgs e)
{
await UpdateFooAsync(new Handler1());
}
public class Handler1 : IHandler
{
public void Success()
{
Provider.Page.text = "hello";
}
public void Error() {}
}
But I might also want to do an asynchronous operation after:
private async void updateAndThenSomething_click(object sender, RoutedEventArgs e)
{
await UpdateFooAsync(new Handler2());
}
// this will generate not implemented error obviously
public class Handler2 : IHandler
{
public async void SuccessAsync()
{
await Provider.OperationAsync();
}
public void Error() {}
}
But FooModel doesn't know whether I want to do a sync or async operation next. And it shouldn't have to know. Is there a way to have handler.Success() run either the synchronous code or the async code? I read some of Stephen Toub's stuff (link) which seems to imply I shouldn't make Success a Task and then await that task in the UpdateFooAsync try block.
I guess the other option is to dispense with the handler interface entirely. But that would require a lot of refactoring. The above situation happens a lot in my code, often with more than just success and error. I want to force someone using my code to specify what happens on success, error, or whatever. That's why I was using the interface. I guess I could also do that by throwing different kind of exceptions, but I'm not sure that's a good idea either.
Upvotes: 3
Views: 66
Reputation: 13495
You can declare your handler interface to return a Task
and then in the synchronous case just return using Task.FromResult
. E.g.
public interface IHandler
{
Task Success();
void Error();
}
public class Handler1 : IHandler
{
public Task Success()
{
Provider.Page.text = "hello";
return Task.FromResult<object>(null);
}
public void Error() {}
}
public class Handler2 : IHandler
{
public async Task Success()
{
return Provider.OperationAsync();
}
public void Error() {}
}
Upvotes: 3