具有重复值的枚举的GetName

如果我在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中找到任何内容,或者在这里有关于复制的枚举这样的“解除引用”,请指点链接,如果我错过了。

实验表明:

V1 =“value1”

V2 =“value1”

但是,这不能保证。 Enum.GetName上的MSDN页面指出:

如果多个枚举成员具有相同的基础值,则GetName方法保证它将返回其中一个枚举成员的名称。 但是,它不保证它始终返回相同枚举成员的名称。 因此,当多个枚举成员具有相同的值时,应用程序代码永远不应该依赖于返回特定成员名称的方法。

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

如果多个枚举成员具有相同的基础值,则GetName方法保证它将返回其中一个枚举成员的名称。 但是,它不保证它始终返回相同枚举成员的名称。 因此,当多个枚举成员具有相同的值时,应用程序代码永远不应该依赖于返回特定成员名称的方法。

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

我不同意其他答案的陈述

……这不保证……

……你不能依赖 ……

以及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设定值。 在某些时候,每个使用新版本的客户都会开始告诉我他们看到AA值而不是AAA 。 所有人都看到AA (没有人看到A )。

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

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

并做了一个测试,以确保Blabla.AAA将输出为AAA 。 问题解决了?

证据

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

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

所以:

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

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

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

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

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

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

虽然我没有在生产代码中尝试过多,但我想知道使用struct而不是enum来获得更多控制会更好……但我认为它需要很多样板代码,这些代码不能轻易地通用。