区分方法签名中的Task和Task<T>

Distinguish Task and Task<T> in method signature

我正在用这个签名创建一个方法

public async Task RunThisFunction(Func<Task> func)

现在这个方法的用户可以使用像这样的函数作为参数

 public async Task GetThingsDone(){await DoSomething();}

这没什么好奇怪的,但你似乎也可以像这样发送函数

public async Task<string> GetString(){await return GetSomething();}

为什么会这样,我将如何编写签名以防止后者被发送到 RunThisFunction?

Task<T>class继承了Taskclass,所以returns一个Task<string>返回一个[=11的方法=].您通常不能在需要更具体类型的地方提供不太具体的类型,因为它没有更具体类型所具有的附加成员。通常,您可以在期望不太具体的类型的地方提供更具体的类型,因为它将具有不太具体的类型所具有的所有成员。如果您有访问 Task class 成员的代码,并且传入 Task<string>,那么该代码将起作用,因为 Task<string> 对象具有所有这些成员。

您可以推断任务的通用参数:

public async Task<TTaskArg> RunThisFunction<TTaskArg>(Func<Task<TTaskArg>> func)
{
    return await func();
}

您可以将其作为现有方法的重载,编译器将根据推断的类型选择正确的方法。

Try it online

注意:虽然我没有在我的可运行示例中使用 async 关键字,但那是因为我的代码本质上是同步的。在实践中,您可能希望使用上面的 asyncawait 关键字。


如果您希望显示编译器警告,您可以添加 [Obsolete] 属性,例如:

[Obsolete("Please do not use tasks with a generic parameter.")]
public async Task<TTaskArg> RunThisFunction<TTaskArg>(Func<Task<TTaskArg>> func)
{
    return await func();
}

也许(如果你需要的话)如果你想在运行时阻止它(成功)使用,你可以从方法中抛出一个 InvalidOperationException

正如 Alexei 所说,您还可以为过时的警告 (CS0612) 启用“警告作为错误”。有关如何执行此操作的说明,请参阅 here。请记住,您需要使用 CS0612 而不是 NU1605。