0

So I have an interface that defines some function including this

Task DoWork();

The problem is that some of the class implementing this interface does not need to do work asynchronously so my current solution is returning Task.CompletedTask after doing some synchronous work.

The question is: Am I doing it in a wrong way, I feel that having an async function doing sync work is kind of wrong

Ahmed Fawzy
  • 309
  • 2
  • 8
  • 1
    Strictly technical, this is fine. It won't cause any issues. It just becomes an issue with readability and preferred convention for your implementations. – Igor Sep 10 '21 at 17:12
  • There is a slight difference in error handling, which might make it desirable to use `async` even if you're not doing asynchronous work (https://stackoverflow.com/a/56490137/120955). Then again, returning Task.CompletedTask is going to have slightly better performance. So there's not a single right answer here. – StriplingWarrior Sep 10 '21 at 17:17

2 Answers2

1

It may feel wrong - it may even BE wrong, but the wrongness is not on your side.

If a method is defined as returning a Task, then this is either you implementing a special case of the method that in this particular case does not have to be async... or it is simply that the definition of the method is wrong and it should not return a Task.

THIS is the semantical challenge. But you implementing it as Task.CompletedTask is just you following in this case the semantics as lined out, as per the C# specifications, actually.

With a concrete example we could discuss whether the method SHOULD return a Task or not... but generally: This happens. Why you think Task.CompletedTask exists?

TomTom
  • 61,059
  • 10
  • 88
  • 148
  • The `Task.CompletedTask` exists for the case where no work has to be done. – Theodor Zoulias Sep 10 '21 at 17:19
  • No, Task.CompletedTask exists also for cases when there is work, but it can be done synchronous, so there is no need for the task to be awaited for. This may be that the method in this particular implementation (of an interface) can do it sync, or for code paths that are not io bound. There is no requirement for not work to be done for using it. I have plenty of code where you call a method, and under some circumstances you get back a completed task immediately (while under other parameters you may have to await the results). – TomTom Sep 10 '21 at 17:29
  • *"Why you think Task.CompletedTask exists?"* I assume that no one here has internal knowledge about the design goals behind the introduction of this API. Also just because an API exists, is not by itself a justification for using it. Otherwise we would all use `ArrayList`s and `SecureString`s every day. – Theodor Zoulias Sep 10 '21 at 18:14
  • " I assume that no one here has internal knowledge about the design goals behind the introduction of this API." - except the team actually talked about it on their blog. – TomTom Sep 10 '21 at 18:17
  • Could you post the link? – Theodor Zoulias Sep 10 '21 at 18:22
  • Nope. This is ancient and MS reorganized all the development blogs. The next time things came up was with the introduction of ValueTask... https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/ or https://devblogs.microsoft.com/dotnet/async-valuetask-pooling-in-net-5/ but the older blogs are simply gone these days. – TomTom Sep 10 '21 at 18:32
1

If your usage of the interface requires the operation be async-friendly, then no it's not wrong. Task.CompletedTask was made available as part of the C# language for a reason. It's important to reflect on usages of it and whether you're really doing something wrong or just conforming to the contract defined by your interface, which you seem to be doing by asking this question.

If you find yourself repeating this pattern often in your interface implementations then perhaps that's an opportunity to rethink your architecture: why you have interfaces that sometimes run synchronously and sometimes run asynchronously and how you can better design a system that handles a concrete implementation that needs to do work asynchronously versus a concrete implementation that doesn't need to run asynchronously.

Andrew H
  • 875
  • 5
  • 20