在具有多个相应名称的枚举值上调用ToString()时,是什么决定选择哪个名称?

sma*_*man 15 .net c# clr enums tostring

在调用具有多个相应名称ToString()enum值时,什么决定选择哪个名称?

问题的详细解释如下.

我已经确定这不是由以下任何一个唯一确定的:字母顺序; 申报单; 名字长度.

例如,考虑我想要一个枚举,其中数值直接对应于实际用途(例如颜色的rgb值).

public enum RgbColor 
{
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff
}
Run Code Online (Sandbox Code Playgroud)

现在,使用此枚举,调用default(RgbColor)将返回黑色的rgb值.假设我不希望默认值为黑色,因为我希望UI设计者能够在没有关于使用什么颜色的具体说明时使用"默认"调用.目前,要使用的UI设计器的默认值实际上是"蓝色",但这可能会改变.所以,我TextDefault在枚举上添加了一个额外的定义,现在它看起来像:

public enum RgbColorWithTextDefaultFirst
{
    TextDefault = 0x0000ff,
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff
}
Run Code Online (Sandbox Code Playgroud)

现在,我已经测试了这个,我发现调用RgbColorWithTextDefaultFirst.TextDefault.ToString()并且RgbColorWithTextDefaultFirst.Blue.ToString()都产生"蓝色".所以,我认为最后声明的名称将覆盖先前声明的名称.为了测试我的假设,我写道:

public enum RgbColorWithTextDefaultLast
{
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff,
    TextDefault = 0x0000ff 
}
Run Code Online (Sandbox Code Playgroud)

然而,令我惊讶的是,RgbColorWithTextDefaultLast.Blue.ToString()RgbColorWithTextDefaultLast.TextDefault.ToString().我的下一个猜测是它按字母顺序对名称进行排序并返回第一个名称.为了测试这个,我尝试:

public enum RgbColorWithATextDefaultFirst
{
    ATextDefault = 0x0000ff,
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff
}

public enum RgbColorWithATextDefaultLast
{
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff,
    ATextDefault = 0x0000ff
}
Run Code Online (Sandbox Code Playgroud)

现在,所有四个RgbColorWithATextDefaultFirst.ATextDefault.ToString(),RgbColorWithATextDefaultFirst.Blue.ToString(),RgbColorWithATextDefaultLast.ATextDefault.ToString(),RgbColorWithATextDefaultLast.Blue.ToString(),我结束了"蓝色".我意识到还有另一个区别因素,即字符串的长度.我的猜测是,所选名称由名称字符串的长度决定.所以,我的测试是使用这些声明:

public enum RgbColorWithALast
{
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff,
    A = 0x0000ff
}

public enum RgbColorWithAFirst
{
    A = 0x0000ff,
    Black   = 0x000000,
    Red     = 0xff0000,
    Green   = 0x00ff00,
    Blue    = 0x0000ff,
    White   = 0xffffff
}
Run Code Online (Sandbox Code Playgroud)

现在,你猜怎么着价值我得到了所有的:RgbColorWithAFirst.A.ToString(); RgbColorWithAFirst.Blue.ToString(); RgbColorWithALast.A.ToString(),RgbColorWithALast.Blue.ToString().那是对的,"蓝色".

在这一点上,我已经放弃试图通过猜测来弄清楚是什么决定了这一点.我打开了反射器,我打算看看并尝试解决这个问题,但我想我会在这里问一个问题,看看这里是否有人已经知道答案了,这又是:什么决定了这个名字是什么在调用具有多个相应名称ToString()enum值时选择?

Aak*_*shM 6

我可能在这里走得太远了,但我认为它是通过二进制搜索排序值来决定的,因此可以取决于值总数的奇偶性.您可以通过在最后一个示例(RgbColorWithAFirstRgbColorWithALast)中通过在两者中定义另一个值来说明这一点- 然后A从所有ToString调用中获取.

我通过反编译mscorlib(4.0)来到这里,并注意到最终我们调用了Array.BinarySearch一个已声明值的已排序数组.当然,二进制搜索一旦匹配就会停止,因此要使它在两个相同的值之间切换,最简单的方法是通过添加额外的值来更改搜索树.

当然,这是一个实现细节,不应该依赖.在我看来,在你的情况下,你最好通过使用DescriptionAttribute你想要明确显示值的枚举值,以及辅助方法,如:

public static class EnumExtensions
{
    public static string Description(this Enum value)
    {
        var field = value.GetType().GetField(value.ToString());
        var attribute = Attribute.GetCustomAttribute(
                            field, 
                            typeof (DescriptionAttribute)) 
                        as DescriptionAttribute;

        return attribute == null ? value.ToString() : attribute.Description;
    }
}
Run Code Online (Sandbox Code Playgroud)