如何在没有警告的情况下实现同步任务返回方法 CS1998.
本文关键字:同步 实现 任务 返回 CS1998 方法 情况下 警告 | 更新日期: 2023-09-27 17:56:15
以以下接口为例:
interface IOracle
{
Task<string> GetAnswerAsync(string question);
}
此接口的某些实现可能使用 async
/await
.其他人可能不需要。例如,考虑这个简单的玩具实现。
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;
}
}
编译器警告 CS1998 当然是有道理的。通常的建议是删除 async
关键字并使用 Task.FromResult
,但它错过了一个微妙的问题。 如果代码引发异常怎么办?然后,该代码转换会更改方法的行为:async
版本会将任何异常包装在Task
中;非async
版本不会,没有明确的try
- catch
.
保留 async
关键字完全按照我的意愿工作,但它会产生编译器警告,我认为抑制这些警告是不明智的。
应该如何重构我的方法实现,使其不产生编译器警告,同时像任何其他async
方法一样用Task
包装所有异常?
我用于从产生编译器警告 CS1998 的async
版本转换为行为相同的非async
版本的机械翻译如下所示。
- 删除
async
关键字。 - 用
try
-catch
包装整个现有方法主体。 - 在
try
-catch
之前定义一个名为tcs
的TaskCompletionSource<T>
。 - 将所有现有的
return <expr>;
实例替换为后跟return tcs.Task;
tcs.SetResult(<expr>);
的 。 - 定义要调用
tcs.SetException(e)
catch
块,后跟return tcs.Task;
。
例如:
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;
}
}
这可以通过以下内容更普遍地表达,尽管我不知道将这样的帮助程序方法实际引入代码库是否合适。
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;
}
如果使用 .NET 4.6,则可以使用 Task.FromException
来处理异常情况,就像使用 FromResult
处理成功案例一样:
public Task<string> GetAnswerAsync(string question)
{
try
{
return Task.FromResult(Lookup[question]);
}
catch(Exception e)
{
return Task.FromException<string>(e);
}
}
如果您使用的是 .NET 4.5,则需要编写自己的 FromException
方法,但这非常简单:
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;
}