C# 8 中可空类型和泛型的问题
A problem with Nullable types and Generics in C# 8
添加 <Nullable>enable</Nullable>
or #nullable enable
后,我 运行 使用我的通用方法解决了以下问题:
这不起作用:
public T? GetDefault<T>()
{
return default;
}
这适用于警告:
public T GetDefault<T>()
{
return default;
}
这可以单独使用,但不能一起使用。
public T? GetDefault<T>() where T : class
{
return default;
}
public T? GetDefault<T>() where T : struct
{
return default;
}
从逻辑上讲,第一种方法应该可行。
在不创建多个方法和抑制警告的情况下,摆脱这种情况的正确方法(在任何框架中)是什么?
[MaybeNull] 属性仅适用于 .Net Core 3.0+。
另外,我问了这个问题here
问题说明
您的第一个代码示例中出现问题是因为编译器以不同方式处理可为 null 的值类型和可为 null 的引用类型:
- 可空值类型
T?
由类型 Nullable<T>
表示。
- 可空引用类型
T?
与 T
类型相同,但带有编译器生成的属性对其进行注释。
编译器无法生成同时涵盖这两种情况的代码,因此会出现编译错误。这个错误迫使我们指定 class
或 struct
约束。此行为也在 C# specification
:
中说明
For a type parameter T
, T?
is only allowed if T
is known to be a value
type or known to be a reference type.
可以在这篇文章中找到对这个问题的很好的解释:Try out Nullable Reference Types。滚动到段落 "The issue with T?
".
解决问题的解决方法
如果您不想创建两个具有不同名称的方法并禁止显示警告,则可以使用下一个解决方法:
// An overload that will be used by reference types.
public T? GetDefault<T>(T? t = default) where T : class
{
return default;
}
// An overload that will be used by value types.
public T? GetDefault<T>(T? t = default) where T : struct
{
return default;
}
这里我们向方法 GetDefault
添加了一个参数 t
以使编译器能够区分这两个方法。现在我们可以使用方法 GetDefault
并且编译器将定义要使用的重载。这种方法的缺点是 GetDefault
方法有不可用的参数 t
.
看来这个问题的最佳解决方案只能在 C# 9 作为 T??
链接:
1. https://github.com/dotnet/csharplang/issues/3471#issuecomment-631722668
2.https://github.com/dotnet/csharplang/issues/3297
目前,Rikki Gibson 提供了一个可行的解决方案。它意味着额外的代码,但它可以正常工作。
T?
只能在已知类型参数是引用类型或值类型时使用。否则,我们不知道是将其视为 System.Nullable<T>
还是可空引用类型 T
.
相反,您可以使用 [MaybeNull]
属性在 C# 8 中表达这种情况。
#nullable enable
using System.Diagnostics.CodeAnalysis;
public class C
{
[return: MaybeNull]
public T GetDefault<T>()
{
return default!; // ! just removes warning
}
}
此属性仅包含在 .NET Core 3.0+ 中,但可以在您的项目内部声明和使用该属性(虽然这不受官方支持,但没有理由假设该行为会破坏线)。为此,您只需在代码中添加一个名称空间+class 声明,类似于以下内容:
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute { }
}
添加 <Nullable>enable</Nullable>
or #nullable enable
后,我 运行 使用我的通用方法解决了以下问题:
这不起作用:
public T? GetDefault<T>()
{
return default;
}
这适用于警告:
public T GetDefault<T>()
{
return default;
}
这可以单独使用,但不能一起使用。
public T? GetDefault<T>() where T : class
{
return default;
}
public T? GetDefault<T>() where T : struct
{
return default;
}
从逻辑上讲,第一种方法应该可行。
在不创建多个方法和抑制警告的情况下,摆脱这种情况的正确方法(在任何框架中)是什么?
[MaybeNull] 属性仅适用于 .Net Core 3.0+。
另外,我问了这个问题here
问题说明
您的第一个代码示例中出现问题是因为编译器以不同方式处理可为 null 的值类型和可为 null 的引用类型:
- 可空值类型
T?
由类型Nullable<T>
表示。 - 可空引用类型
T?
与T
类型相同,但带有编译器生成的属性对其进行注释。
编译器无法生成同时涵盖这两种情况的代码,因此会出现编译错误。这个错误迫使我们指定 class
或 struct
约束。此行为也在 C# specification
:
For a type parameter
T
,T?
is only allowed ifT
is known to be a value type or known to be a reference type.
可以在这篇文章中找到对这个问题的很好的解释:Try out Nullable Reference Types。滚动到段落 "The issue with T?
".
解决问题的解决方法
如果您不想创建两个具有不同名称的方法并禁止显示警告,则可以使用下一个解决方法:
// An overload that will be used by reference types.
public T? GetDefault<T>(T? t = default) where T : class
{
return default;
}
// An overload that will be used by value types.
public T? GetDefault<T>(T? t = default) where T : struct
{
return default;
}
这里我们向方法 GetDefault
添加了一个参数 t
以使编译器能够区分这两个方法。现在我们可以使用方法 GetDefault
并且编译器将定义要使用的重载。这种方法的缺点是 GetDefault
方法有不可用的参数 t
.
看来这个问题的最佳解决方案只能在 C# 9 作为 T??
链接:
1. https://github.com/dotnet/csharplang/issues/3471#issuecomment-631722668
2.https://github.com/dotnet/csharplang/issues/3297
目前,Rikki Gibson 提供了一个可行的解决方案。它意味着额外的代码,但它可以正常工作。
T?
只能在已知类型参数是引用类型或值类型时使用。否则,我们不知道是将其视为 System.Nullable<T>
还是可空引用类型 T
.
相反,您可以使用 [MaybeNull]
属性在 C# 8 中表达这种情况。
#nullable enable
using System.Diagnostics.CodeAnalysis;
public class C
{
[return: MaybeNull]
public T GetDefault<T>()
{
return default!; // ! just removes warning
}
}
此属性仅包含在 .NET Core 3.0+ 中,但可以在您的项目内部声明和使用该属性(虽然这不受官方支持,但没有理由假设该行为会破坏线)。为此,您只需在代码中添加一个名称空间+class 声明,类似于以下内容:
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
internal sealed class MaybeNullAttribute : Attribute { }
}