不允许为可为空的枚举返回 null

JHB*_*ius 2 c# generics enums nullable

我编写了一个简单的扩展方法来帮助将字符串解析为可为空的枚举类型。

public static TEnum? ParseNullableEnum<TEnum>(this string? str)
    where TEnum : Enum
{
    if (str is null)
    {
        return null;
    }

    if (!Enum.TryParse(typeof(TEnum), str, ignoreCase: true, out var source))
    {
        throw new ArgumentOutOfRangeException(nameof(str), str, null);
    }

    return (TEnum)source;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,返回类型是TEnum?,其中TEnumEnum。并且启用了 nullable,因此 null 是一个有效值。但是,我在网上遇到错误return null;

CS0403:无法将 null 转换为类型参数“TEnum”,因为它可能是不可为 null 的值类型。考虑使用 default('TEnum') 代替。

这是VS的bug,还是我做错了什么?我没有转换为TEnum,输出类型是TEnum?...对吗?

can*_*on7 5

你要:

public static TEnum? ParseNullableEnum<TEnum>(this string? str)
    where TEnum : struct, Enum
Run Code Online (Sandbox Code Playgroud)

您遇到的问题是Enum约束可以匹配任何特定的枚举类型或Enum类本身。(Enum这是一个奇怪的类型:它是所有枚举类型的基本类型,但它本身是一种引用类型。它基本上代表一个装箱枚举类型,即您可以编写Enum foo = condition ? ConsoleColor.Black : ConsoleKey.A;, 来选择两个随机不同的枚举类型)。

换句话说,有人可以调用:

ParseNullableEnum<Enum>("thing")
Run Code Online (Sandbox Code Playgroud)

添加struct约束会强制某人实际传递特定的枚举类型,而不是Enum.

难题的另一部分是,如果泛型类型参数T不限于classor struct,则T?意味着它T是“可默认的”而不是“可为空的”。这意味着:

  • 如果T是引用类型,T?则允许null赋值
  • 如果T是值类型,T? 并不意味着Nullable<T>,它只是意味着与 相同T

这是由于T?编译器对引用类型和值类型的实现方式不同所致。对于值类型,T?是 type 的简写Nullable<T>,但对于引用类型,T?编译为相同的内容,T但仅对编译器警告产生影响。编译器不可能发出返回类型为TwhenT是引用类型,但Nullable<T>whenT是值类型的方法。

因此,通过不限制T引用或值类型,您的返回类型TEnum?并不意味着Nullable<TEnum>,它只是意味着TEnum不同的可为空警告。通过限制TEnum值类型,编译器可以发出实际返回 的方法Nullable<TEnum>,因此允许具有值null