可能 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 可以是任何类型,包括可为空的引用类型或可为空的值类型。
明确地说,我理解为什么此方法会触发警告。我想知道的是用什么注解可以解决。
- 我知道我可以使用#nullable disable 或
default(T)!
,但我希望得到的东西不是 "hammer"。
- 我知道我不能使用
[return: MaybNull]
,因为那将适用于 Task
本身,而不适用于 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?>();
我正在将代码库转换为具有可空引用类型的 C#8。我遇到了一种类似于
public async Task<T> GetAsync<T>()
{
// sometimes returns default(T); => warning CS8603 Possible null reference return
}
T 可以是任何类型,包括可为空的引用类型或可为空的值类型。
明确地说,我理解为什么此方法会触发警告。我想知道的是用什么注解可以解决。
- 我知道我可以使用#nullable disable 或
default(T)!
,但我希望得到的东西不是 "hammer"。 - 我知道我不能使用
[return: MaybNull]
,因为那将适用于Task
本身,而不适用于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?>();