任务的可空性警告.FromResult



我们的代码库(.NET Standard 2.0库(中有以下方法:

public Task<T> GetDefaultTask<T>()
{
return Task.FromResult(default(T));
}

我们目前正在尝试转换到C#8.0的Nullability,并在上面的代码中得到警告:

警告CS8604:"任务任务"中参数"result"的引用参数可能为null。FromResult(T结果('。

为什么会收到此警告?对我来说,将null作为参数传递给Task.FromResult看起来非常好。

重要提示:我们希望允许Task包含null值。但是添加Task<T?>会迫使我们添加类型约束,而这是我们无法做到的

如果T是不可为null的引用类型,则不应将null传递给Task.FromResult<T>Task.FromResult的实现不关心空引用,您可以使用Task.FromResult(default(T)!),但GetDefaultTask的调用方可能会收到Task<string>,而实际上它应该是Task<string?>。像GetDefaultTask<string>().Result.Length这样的代码会在没有警告的情况下编译,并在运行时导致null引用异常。

据我所知,目前在这种情况下不可能正确地注释返回类型。

不允许将方法声明为Task<T?> GetDefaultTask<T>(),因为T可以是结构或引用类型,并且可为null的结构和引用类型的表示方式不同。

如果T被约束为一个参考类型:,则可以干净地解决这个问题

public Task<T?> GetDefaultTask<T>() where T : class

但是,根据T参数的来源,添加该约束可能会导致调用链上的问题。

对于泛型返回值可以是结构或引用(如Enumerable.FirstOrDefault(的类似情况,有[MaybeNull]属性,但它只能应用于返回值本身(在这种情况下是任务(,而不能应用于任务的泛型参数。

对我来说,将null作为参数传递给Task.FromResult看起来非常好。

不,这是个坏主意。

如果调用者为T指定了一个不可为null的类型,那么default(T)可以被认为是"未定义的"(它实际上是null,但这是C#8.0实现不可为Null的引用类型的一个主要缺点(即它们可以仍然是null,grrrr(。考虑:

// Compiled with C# 8.0's non-nullable reference-types enabled.
Task<String> task = GetDefaultTask<String>();
String result = await task;
Console.WriteLine( result.Length ); // <-- NullReferenceException at runtime even though the C# compiler reported `result` cannot be null.

对于没有足够类型约束的泛型类型,避免在C#8.0中使用default/default(T)

这个问题有几个解决方案:

1:指定调用者提供的默认值:

public Task<T> GetDefaultTask<T>( T defaultValue )
{
return Task.FromResult( defaultValue );
}

因此,调用站点需要更新,如果调用方试图在运行时使用null而不是异常,C#编译器将发出警告或错误:

Task<String> task = GetDefaultTask<String>( defaultValue: null ); // <-- compiler error or warning because `null` cannot be used here.
String result = await task;
Console.WriteLine( result.Length );

2:在不同的方法上添加结构与类的约束:

struct/值类型的default(T)可能是有意义的(或者它可能和null一样危险…(,因为我们可以安全地使用default(T)(其中T : struct,而不是default(T),其中T : class(,我们可以为这种情况添加不同的重载:

public Task<T> GetDefaultTask<T>()
where T : struct
{
return Task.FromResult( default(T) );
}
public Task<T> GetDefaultTask<T>( T defaultValue )
where T : class
{
return Task.FromResult( defaultValue );
}

(请注意,不能纯粹基于泛型类型约束来重载方法——只能通过泛型参数计数和通常的参数类型来重载

相关内容

  • 没有找到相关文章

最新更新