具有重复值的枚举的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
来获得更多控制会更好……但我认为它需要很多样板代码,这些代码不能轻易地通用。