枚举名称的优先顺序

时间:2013-05-12 01:31:39

标签: c# enums

鉴于以下枚举:

[Flags]
public enum Intervals
{
  Root = PerfectUnison,
  Unison = PerfectUnison,
  PerfectUnison = 1 << 0,
  AugmentedUnison = MinorSecond,

  MinorSecond = 1 << 1,
  Second = MajorSecond,
  MajorSecond = 1 << 2,
  AugmentedSecond = MinorThird,

  MinorThird = 1 << 3,
  Third = MajorThird,
  MajorThird = 1 << 4,
  AugmentedThird = PerfectFourth,
  DoubleAugmentedThird = Triton,

  DiminishedFourth = MajorThird,
  Fourth = PerfectFourth,
  PerfectFourth = 1 << 5,
  AugmentedFourth = Triton,
  DoubleAugmentedFourth = PerfectFifth,

  Triton = 1 << 6,

  //...Removed for brevity, see link to code bellow 
}

我正在尝试这个简单的测试:

static void Main(string[] args)
{
  var values = Enum.GetValues(typeof(Intervals));
  foreach (var value in values)
  {
    Console.WriteLine(value);
  }
}

这是输出:

  

PerfectUnison,PerfectUnison,PerfectUnison,AugmentedUnison,AugmentedUnison,Second,Second,MinorThird,MinorThird,DiminishedFourth,DiminishedFourth,DiminishedFourth,AugmentedThird,AugmentedThird,AugmentedThird,AugmentedThird,DoubleDiminishedSixth,DoubleDiminishedSixth等。

虽然我希望为相同值选择的枚举名称具有以下顺序:

  

Root,MinorSecond,Second,MinorThird,Third,Fourth,Triton,Fifth,MinorSixth,Sixth,Minor第七,第七,Octave,MinorNinth,第九,第十,第十一,MajorEleventh,十三

良好的复制也是Enum.GetNames。我希望上面组的名称总是应该在它们的值匹配名称之前。

我基本上是在寻找每个值的枚举名称的优先级/优先级规则的文档。

您可以在此处使用代码:http://rextester.com/EJOWK87857

更新

我现在正在研究反编译的Enum.GetNames。看起来它使用反射。那么问题就是“如何控制反射场的顺序?”。

1 个答案:

答案 0 :(得分:5)

不使用元数据,这是不可能的,因为编译器可能会为每个枚举成员分配常量值。检查已编译的IL表明编译代码时分配信息丢失:

.field public static literal valuetype .../Intervals Unison = int32(1)    
.field public static literal valuetype .../Intervals PerfectUnison = int32(1)
.field public static literal valuetype .../Intervals AugmentedUnison = int32(2)
...

由于在编译源时(或者至少不保证可用)丢失此信息,因此无法在运行时根据赋值分配优先级规则。此限制与Enum.ToString()的文档一致,该文档指出如果多个名称与相同的值相关联,则所选的成员是不确定的:

  

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

这就是说,一种可能的解决方法可能是将属性值分配给被认为是赋值优先级的枚举值。例如:

[AttributeUsage(AttributeTargets.Field)]
class PriorityAttribute : Attribute { }
[Flags]
public enum Intervals
{
    Root = PerfectUnison,
    Unison = PerfectUnison,
    [Priority]
    PerfectUnison = 1 << 0,
    AugmentedUnison = MinorSecond,

    [Priority]
    MinorSecond = 1 << 1,
    Second = MajorSecond,
    [Priority]
    MajorSecond = 1 << 2,
    AugmentedSecond = MinorThird,
    ...

由于属性信息在运行时与枚举值相关联,因此可以在运行时访问标记的枚举名称:

typeof(Intervals)
    .GetFields()
    .Where(a => a.GetCustomAttributes(typeof(PriorityAttribute), false).Length > 0)
    .Select(a => a.Name))

同样,您可以向Enum.GetName写一个类似物,仅返回定义了属性的名称(例如,GetPriorityName(typeof(Intervals), 1)将始终返回PerfectUnison

static string GetPriorityName(Type enumType, object v)
{
    Type ut = Enum.GetUnderlyingType(enumType);
    var pty = enumType.GetFields()
        .Where(
            a => a.IsLiteral 
            && a.GetRawConstantValue().Equals(v)
            && a.GetCustomAttributes(typeof(PriorityAttribute), false).Length > 0
            )
        .FirstOrDefault();
    if (pty == null) 
        return Enum.GetName(enumType, v); // default to standard if no priority defined
    return pty.Name;
}