1

I have a simple set of code where I'm trying to implement an interface with an async method on it. The implementations thus far have all been things that utilize the async part of it and end up awaiting things. However, this new implementation of the interface is a simple synchronous operation-- no need for any awaiting.

When I implement it, first of all feels weird. Second, Visual Studio affirms my uncomfort with a warning telling me that the method is going to run synchronously-- which is what I'd expect, but the warning tells me it smells bad to VS, too. Is there a better pattern I should be following?

public interface IActivity
{
    async Task<bool> DoStuff();
}

//...

List<IActivity> activities = new List<IActivity>(){
   new SynchronousActivityA(),
   new SynchronousActivityB(),
   new AsynchronousActivityC()
};

foreach (var activity in activities){
    bool stuff = await activity.DoStuff();
    //do things with stuff.
}

I could just make my DoStuff() implementation do something like this:

await Task.Run(() => DoStuffSynchronously());

This would make my warning go away and feel a little more "right", but I'm not sure what benefit this would have over just writing the DoStuff() implementation to be as though it was a synchronous signature.

scott degen
  • 125
  • 1
  • 1
  • 7
  • 2
    Don't include `async` in the interface definition; only where you need to use the `await` keyword, which is the implementation of the interface. – Kenneth K. Jun 21 '19 at 21:10
  • Remove the async keyword from the interface, that should be left up to the implementation. – Dan D Jun 21 '19 at 21:10
  • Why are your synchronous operations implementing an asynchronous interface? Either have a synchronous interface for them to implement, since that's the behavior that they have, or *provide an asynchronous implementation*, because that's the contract they're agreeing to meet by implementing the interface. Anyone using this interface is going to expect any implementation to be asynchronous, because that's what it claims. You're breaking their code by not meeting that expectation. – Servy Jun 21 '19 at 21:12
  • 4
    Error CS1994 The 'async' modifier can only be used in methods that have a body. – H H Jun 21 '19 at 21:20
  • `async Task DoStuff();` on the interface doesn't even compile. It fails on a `CS0106` error; `The modifier 'async' is not valid for this item`. – pfx Jun 21 '19 at 21:22
  • You have told us a story, told us your thoughts, told us about the error, but you haven't stated what you are actually trying to do. Do you want some sort of parrallelism? Do you want the async and await pattern? – TheGeneral Jun 21 '19 at 21:32
  • Possible duplicate. Here's a popular question which covers the same topic with an answer from Stephen Cleary, who is quite knowledgeable: [Link](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – John Wu Jun 21 '19 at 21:32

1 Answers1

7

interface with an async method

Technically, "a method that returns Task<T>". async and await are implementation details.

A method returning an awaitable is a method that may be asynchronous. Synchronously returning a completed task is perfectly acceptable.

One way to do this is using Task.FromResult, but you would also want to be sure to capture exceptions and return them on the task as well:

try { return Task.FromResult(DoStuffSynchronously()); }
catch (Exception ex) { return Task.FromException<bool>(ex); }

This is a bit wordy. It does essentially the same thing as async without await, but you'd need to #pragma away the warning message, which is also awkward. If you do this often, you may want to define a utility method for it, something like TaskHelper.ExecuteAsTask in my AsyncEx library.

You should not use Task.Run just to "make code asynchronous". Task.Run uses a thread pool thread, which is not necessary in this case.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • "A method returning an awaitable is a method that may be asynchronous. Synchronously returning a completed task is perfectly acceptable". Very interesting! Is there a source of material on this? I have not found a good source yet in terms of design. Let's say designing an interface for file storage and one implementation is local file based and another HTTP, one can be synchronous, the other asynchronous, but would require two interfaces and more difficult code. It violates the "make synchronous library" methods because you might have to use it in a sync codebase. Seems like a complex topic? – David Anderson Jul 20 '22 at 18:52
  • @DavidAnderson: `source of material` - I'm not aware of anything official; just [my blog](https://blog.stephencleary.com/2013/10/taskrun-etiquette-and-proper-usage.html). If you *know* one of your implementations is synchronous, then I'd recommend calling this out in the documentation but keeping the interface asynchronous. Note that this is a rare scenario. – Stephen Cleary Jul 21 '22 at 22:17