如何将 `Task<string>` 实例用作方法的 Task<string?> 参数?

How can I use a `Task<string>` instance as a Task<string?> parameter to a method?

如何使用 Task<string> 实例作为方法的 Task 参数?

如果我启用了可空性,并且我有两个异步方法,例如..

// in an ordinary class
public async static Task<string> Foo() { ... }

// in a static class
public async static Task Bar(this Task<string?> task1) { ... }

我尝试调用 await Foo().Bar();,但编译器给我:

warning CS8620: Argument of type 'Task<string>' cannot be used for parameter 'task' of type 'Task<string?>'

我可以对 Foo 的结果做些什么才能使其作为 Task 接受?

您可以将 Task<string> 包装在 return 作为 Task<string?> 结果的延续中:

await Foo().ContinueWith<string?>(task => {
    try {
        return task.Result;
    } catch (AggregateException e) {
        throw e.InnerExceptions[0]; // Propagate exceptions/cancellation.
    }
}).Bar();

ContinueWith 中的 lambda 表达式必须 return 一个 string?,它在这里起作用,因为 task.Result 是一个 string,它可以隐式转换为 string?.

有人提议Task nullability covariance

目前,您必须使用 ! (null-forgiving) operator 来消除编译器警告:
await FooClass.Foo()!.Bar();

class Program
{
    static async Task Main(string[] args)
    {
        await FooClass.Foo()!.Bar();
    }
}

public static class Extensions
{
    public async static Task Bar(this Task<string?> task)
    {
        System.Console.WriteLine("bar");
        await Task.CompletedTask;
    }
}


public class FooClass
{
    public async static Task<string> Foo()
    {
        System.Console.WriteLine("foo");
        return await Task.FromResult("");
    }
}

Full example on SharpLab

问题是 C# 的可空引用类型注释不是泛型类型的变体。

这段代码证明了这一点:

Task<object> o = Task.FromResult<object>(new object());
Task<object?> p = o;

虽然我们知道一个不能 return null 的任务应该被允许代替一个可以 return null 的任务,但编译器并不知道这一点。

编译这个gives warning

CS8619: Nullability of reference types in value of type 'Task' doesn't match target type 'Task<object?>'.

请注意,此差异适用于委托和接口。但是 Task<T> 两者都不是。

例如,这两个 compile without warning:

IEnumerable<object> c = new[] { new object() };
IEnumerable<object?> d = c;

Action<object?> a = _ => {};
Action<object> b = a;

已经讨论过在 Task<T> 中针对 T 的可空性向编译器添加特殊处理,但尚未落地。

到那个时候,您必须使用 null 宽恕运算符来抑制这些警告。