可能 return default(T) 的异步泛型方法的正确可为空注释

Proper nullable annotation for async generic method that may return default(T)

我正在将代码库转换为具有可空引用类型的 C#8。我遇到了一种类似于 但异步的方法。

public async Task<T> GetAsync<T>()
{
    // sometimes returns default(T); => warning CS8603 Possible null reference return
}

T 可以是任何类型,包括可为空的引用类型或可为空的值类型。

明确地说,我理解为什么此方法会触发警告。我想知道的是用什么注解可以解决。

还有其他 attribute/annotation 我可以申请让编译器满意,还是 default(T)! 我唯一的选择?

通过四处搜索和进行更多研究,似乎在这种情况下抑制警告的首选方法是在 default(T).

上使用 ! 运算符

根据我的经验,您可以使用 Task<T?> GetAsync<T>() where T: class 来解决您的问题。

C# 9中我们可以通过添加?

来解决
public async Task<T?> GetAsync<T>()
{
    return default;
}

但是你需要在调用代码中区分可为空的值类型和可为空的引用类型。要将可空引用类型作为 return 值,您可以使用 <T><T?>:

调用该方法
SomeClass? c = await GetAsync<SomeClass>(); // return type is SomeClass?
SomeClass? c2 = await GetAsync<SomeClass?>(); // return type is SomeClass?

要获得可为 null 的值类型,您需要使用 <T?>:

调用它
int? i = await GetAsync<int?>(); // return type is int?
int i2 = await GetAsync<int>(); // return type is int

P.S。我想知道 Microsoft 如何解释我们 why they can't allow unconstrained T? 然后在下一个 C# 版本中这样做:)

另一种选择是使用 C# 8 答案中的代码。

C# 8 的答案

我们不能有 async Task<T?> GetAsync<T>(),因为 SomeClass?SomeStruct? 非常不同。 default! 也不是最佳选择,因为我们可以通过在调用代码中调用 GetAsync<SomeClass>() 来获取非可空引用类型的可空引用。

更好的选择是让两个不同的方法使用相同的私有方法:

public class Storage
{
    ...
    public Task<T?> GetClassAsync<T>() where T : class
    {
        return GetAsync<T?>();
    }
    public Task<T?> GetStructAsync<T>() where T : struct
    {
        return GetAsync<T?>();
    }
    private async Task<T> GetAsync<T>()
    {
        if (condition)
            return default!;
        string json = await GetJsonAsync();
        T result = JsonSerializer.Deserialize<T>(json);
        return result;
    }
}

和用法:

// return type is SomeClass?
SomeClass? classResult = await storage.GetClassAsync<SomeClass>();

// return type is int?
int? structResult = await storage.GetStructAsync<int>();


// warning: Nullability of type argument doesn't match 'class' constraint
SomeClass? classResult = await storage.GetClassAsync<SomeClass?>();
// error: The type 'int?' must be a non-nullable value type
int? structResult2 = await storage.GetStructAsync<int?>();