为什么在 .NET 5 中指定循环枚举值时枚举的顺序很重要?

Jea*_*tte 2 c# enums .net-5

我正在使用枚举循环引用将一些现有的枚举归结为更少的值。它对我的问题很有效,因为它是一个过渡期,旧的枚举值确实出现在历史中,但不会创建具有过时值的新条目。

我不是在寻找替代方法,但我偶然发现了这个奇怪的问题,其中枚举的顺序会以意想不到的方式影响序列化值。

我有这个枚举:

    public enum CivilStatusEnum
    {
        None = 0,
        Married = 1,
        Cohabiting = Married,
        Alone = 3,
        DivorcedOrSeparated = Alone,
        WidowOrWidower = Alone,
    }
Run Code Online (Sandbox Code Playgroud)

我分配了“DivorcedOrSeparated”= Alone (3)。现在,当我将枚举转换为字符串时,我得到值“DivorcedOrSeparated”。

Console.PrintLine(CivilStatusEnum.Alone.ToString());
Run Code Online (Sandbox Code Playgroud)

输出:

DivorcedOrSeparated
Run Code Online (Sandbox Code Playgroud)

我有一个示例代码,其中包含测试及其预期结果。如您所见,测试失败。但是如果我改变枚举的顺序,测试就会解析。

在此处输入图片说明

    [Theory]
    [InlineData(CivilStatusEnum.Alone, "Alone")]
    [InlineData(CivilStatusEnum.DivorcedOrSeparated, "Alone")]
    [InlineData(CivilStatusEnum.WidowOrWidower, "Alone")]
    [InlineData(CivilStatusEnum.None, "None")]
    [InlineData(CivilStatusEnum.Married, "Married")]
    [InlineData(CivilStatusEnum.Cohabiting, "Married")]
    public void Test(CivilStatusEnum input, string expected)
    {
        var text = input.ToString();
        text.Should().Be(expected);
    }
Run Code Online (Sandbox Code Playgroud)

我似乎无法找到一个合理的解释,说明为什么顺序对 tostring 和 serilization 很重要。

这是 .NET 5 中的错误,还是我遗漏了什么?如果这种行为是故意的,它如何确定哪个枚举名称将是 tostring 的输出?

谢谢 :)

Kri*_*ten 5

Enum.ToString 执行二分查找。

实际上,ToString调用InternalFormat,后者调用GetEnumName。该方法在 EnumInfo.Values 返回的数组中执行二分搜索。

我假设数组以基础值的递增顺序填充(否则二进制搜索将不起作用),并且如果它们相等,则以在源代码中声明的值的顺序填充。这使得搜索结果依赖于声明的顺序。

为了说明这种二分搜索的效果,请考虑以下两个enum定义:

enum Test1 { A = 0, B = 0, C = 0 }
enum Test2 { A = 0, B = 0, C = 0, D = 0, E = 0 }
Run Code Online (Sandbox Code Playgroud)

会是什么结果Test1.A.ToString()?需要注意的是值Test1.A0。二分查找将首先考虑列表中间的元素,它B的值为0。该值等于我们正在搜索的值,因此Test1.A.ToString()返回"B"。如果找到的值高于正在搜索的值,搜索将在列表的下半部分继续。如果找到的值低于正在搜索的值,则搜索将在列表的上半部分继续。

枚举中的所有常量也是如此,因为它们都具有相同的值。所以,Test1.C.ToString()同样会返回"B"

同样,如预期的那样Test2.A.ToString()返回"C"

但是请注意,虽然此行为在 .NET 的当前版本中似乎是可以预测的,但它是undefined,并且可能会在未来版本中更改。

这不是 .NET 5 中的错误。 毕竟,以下两种情况都不可能为真:

CivilStatusEnum.Alone.ToString() ==  "Alone"
CivilStatusEnum.DivorcedOrSeparated.ToString() == "DivorcedOrSeparated"
Run Code Online (Sandbox Code Playgroud)

原因当然是CivilStatusEnum.Alone == CivilStatusEnum.DivorcedOrSeparated

以下是文档对此的说明:

如果多个枚举成员具有相同的基础值,并且您尝试根据其基础值检索枚举成员名称的字符串表示形式,则您的代码不应假设该方法将返回哪个名称。