How to implement a synchronous task return method without warning CS1998?

Take for example the following interface:

interface IOracle { Task<string> GetAnswerAsync(string question); } 

Some implementations of this interface may use async / await . Others may not need. For example, consider this simple toy.

 class SimpleOracle { public Dictionary<string, string> Lookup { get; set; } // Warning CS1998: This async method lacks 'await' operators // and will run synchonously. public async Task<string> GetAnswerAsync(string question) { string answer = Lookup[question]; return answer; } } 

A warning about the CS1998 compiler, of course, makes sense. The usual suggestion is to remove the async and use Task.FromResult , but it does not correspond to the subtle problem. What if the code throws an exception? Then this code conversion changes the behavior of the method: the async version completes any exception in the Task ; the non- async version will not async without try - catch explication.

Leaving the async keyword works exactly the way I want, but it raises a compiler warning, and I don't think suppressing them is reasonable.

How can I reorganize my method implementation so as not to cause a compiler warning, as well as wrapping all exceptions with Task , like any other async method?

+6
source share
2 answers

The mechanical translation that I use to convert from an async version that gives CS1998 a warning to the compiler for a version other than async that behaves the same way is as follows.

  • Delete the async .
  • Wrap the entire existing method object with try - catch .
  • Define a TaskCompletionSource<T> called tcs before try - catch .
  • Replace all existing instances return <expr>; on tcs.SetResult(<expr>); , and then return tcs.Task; .
  • Define a catch to call tcs.SetException(e) , followed by return tcs.Task; .

For instance:

 public Task<string> GetAnswerAsync(string question) { var tcs = new TaskCompletionSource<string>(); try { string answer = Lookup[question]; tcs.SetResult(answer); return tcs.Task; } catch (Exception e) { tcs.SetException(e); return tcs.Task; } } 

This can be expressed as a whole as follows: although I do not know, it would be advisable to actually introduce such an auxiliary method into the code base.

 public static Task<T> AsyncPattern(Func<T> func) { var tcs = new TaskCompletionSource<T>(); try { tcs.SetResult(func()); } catch (Exception e) { tcs.SetException(e); } return tcs.Task; } 
+5
source

If you are using .NET 4.6, you can use Task.FromException to handle the exception case, just like you use FromResult to handle the successful event:

 public Task<string> GetAnswerAsync(string question) { try { return Task.FromResult(Lookup[question]); } catch(Exception e) { return Task.FromException<string>(e); } } 

If you are using .NET 4.5, you need to write your own FromException method, but this is pretty trivial:

 public static Task<T> FromException<T>(Exception e) { var tcs = new TaskCompletionSource<T>(); tcs.SetException(e); return tcs.Task; } public static Task FromException(Exception e) { var tcs = new TaskCompletionSource<bool>(); tcs.SetException(e); return tcs.Task; } 
+3
source

Source: https://habr.com/ru/post/989154/


All Articles