【问题标题】:GetName for enum with duplicate values具有重复值的枚举的 GetName
【发布时间】:2012-03-17 23:09:25
【问题描述】:

如果我在 C# 枚举中有重复的值,说

enum MyE {
  value1 = 1,
  value2 = 2,
  valued = 1
}

以下字符串的值应该是什么?

MyE N = (MyE)1;
string V1 = N.ToString();
string V2 = GetName(MyE, 1);

V1 和 V2 是否必须包含相同的值? 这些值应该是什么?

我在 MSDN 或这里没有找到任何关于这种对带有重复的枚举的“取消引用”的内容,如果我错过了,请给我一个链接。

【问题讨论】:

    标签: c#


    【解决方案1】:

    实验表明:

    V1 = "值1"

    V2 = "值1"

    但是,这不能保证。 MSDN page on Enum.GetName 声明:

    如果多个枚举成员具有相同的基础值,GetName 方法保证它将返回这些枚举成员之一的名称。但是,它不保证它总是返回同一个枚举成员的名称。因此,当多个枚举成员具有相同的值时,您的应用程序代码不应依赖于返回特定成员名称的方法。

    【讨论】:

    • 谢谢,这正是我所需要的。顺便说一句,对于我的真实枚举,GetName 和显式转换都返回第二个合适的枚举成员的名称。
    【解决方案2】:

    我不同意其他答案陈述

    ...这不能保证...

    ...你不能依赖那个...

    以及用msdn声明:

    ...您的应用程序代码不应依赖于返回特定成员名称的方法...

    故事

    我的软件中有一个枚举

    enum Blabla { A = 0, B = 1, C = 2, D = 3 }
    

    在某些时候A 值更改为AA,随后AA 更改为AAA。为了保持向后兼容性,我必须这样做

    enum Blabla { A = 0, AA = 0, AAA = 0, B = 1, C = 2, D = 3 }
    

    这允许将旧的枚举值(由旧版本的软件生成)反序列化为AAA

    然后有一个打印Blabla设置值的报告。在某些时候,每个使用新版本的客户都会开始告诉我,他们看到的不是AAA,而是AA 的价值。他们都看到了AA(没有人报告看到A)。

    我做了什么?我只是改变了顺序(直到结果是AAA

    enum Blabla { AAA = 0, A = 0, AA = 0, ...}
    

    并进行了测试以确保Blabla.AAA 将输出为AAA。问题解决了吗?

    证明

    查看Enum.ToString()(或Enum.GetName())的来源,它使用GetEnumName(),它调用Array.BinarySearch() 对值的排序数组以查找值的索引。

    二分查找的结果是确定性的:为它提供相同的参数将返回相同的结果

    所以:

    • 如果您不更改枚举,那么结果将是相同的。
    • 可以通过实验找到结果(或者通过了解binary search 的工作原理以及枚举的处理方式)。
    • 没有简单的规则来确定结果(例如,您不能说“它总是返回第一个定义的值”)。
    • 不太可能更改enum 的格式(例如,定义顺序和值列表的顺序不同)或Enum.ToString() 将更改,但它可能会发生,因此请确保您对依赖的情况进行了测试返回值。

    【讨论】:

    • 在这种情况下,«这不能保证»意味着,如果您的客户拥有仍然能够运行您的应用程序的较新/较旧版本的 .Net 框架,他们可以获得另一个名称对于枚举值。该算法在相同的设置中肯定是确定性的,但行为不是由某些标准固定的,并且可以随着时间和框架版本而轻松改变。您应该以一种不会因该更改而破坏任何内容的方式编写您的应用程序——这就是“不保证”和“不能依赖”的意思。
    • 但我明白你的意思)确实,原始问题中的函数应该返回相同的值......
    【解决方案3】:

    来自Enum.GetName 方法文档 (http://msdn.microsoft.com/en-us/library/system.enum.getname.aspx) 的“备注”部分,它说:

    如果多个枚举成员具有相同的基础值,GetName 方法保证它将返回这些枚举成员之一的名称。但是,它不保证它总是返回同一个枚举成员的名称。因此,当多个枚举成员具有相同的值时,您的应用程序代码不应依赖于返回特定成员名称的方法。

    我运行了一个测试,看看实验会发生什么,它总是返回定义的第一个值(在你的例子中,value1),但根据上面的官方文档,你不能依赖它 (参见@gluk47 的评论,表明在野外有不同的行为)。

    【讨论】:

    • 根据我的经验,我们不能依赖,MSDN是对的)我的VS是2008,我从枚举中得到了第二个值。我猜想,名称选择是基于它在其他枚举成员名称中的字典序号,但经过一些测试,这似乎也不正确。
    【解决方案4】:

    好吧,因为没有关于顺序的保证,并且过时的属性似乎对Enum 方法没有影响,我通常建议使用字符串属性进行序列化,并使用自定义代码来处理过时的值。

    这样,主Enum 没有重复项,并且只包含当前值。

    根据序列化框架,您可能能够将字符串属性标记为可序列化但已过时,并且代码中使用的实际属性将被序列化忽略。

    要处理字符串属性设置器中的过时值,您可以使用 [Obsolete] enum 或 switch 语句。通常,如果我有很多过时的值要处理,我可能会使用前者,而如果我只有几个值要处理,我可能会使用后者。

    另外,将代码放在帮助类或扩展方法中可能是有意义的,特别是如果您从一堆地方加载或存储该值。

    虽然我没有在生产代码中尝试过太多,但我想知道使用struct 而不是enum 是否会更好地进行控制...但我认为它需要大量样板不容易被通用化的代码。

    【讨论】:

      【解决方案5】:

      虽然这对派对来说可能有点晚了,但这可能不是最好的主意,但是,在我不得不使用重复枚举的情况下,我确实发现的一个解决方法是使用以下来自这里的问题; Get String Name from Enum in C#

      var name = nameof(DeclaredEnum.EnumName);
      

      导致:

      EnumName
      

      您可以阅读更多Here 以了解其实际工作原理。

      【讨论】:

      • OP 似乎在询问一种在运行时以编程方式为给定(未知)数值恢复 Enum 名称的方法;您提供的方法仅适用于编译器中的静态值。
      猜你喜欢
      • 1970-01-01
      • 2014-09-19
      • 2012-08-31
      • 1970-01-01
      • 2017-03-25
      • 2011-08-09
      • 2019-07-18
      • 1970-01-01
      • 2016-03-05
      相关资源
      最近更新 更多