枚举名称的优先级
本文关键字:优先级 枚举 | 更新日期: 2023-09-27 18:13:14
给定以下enum:
[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, minor - third, minor - third, DiminishedFourth, AugmentedThird, AugmentedThird, AugmentedThird, DoubleDiminishedSixth, DoubleDiminishedSixth等
而我希望为相同值选择的enum名称具有以下顺序:
大调,小调二,二,小调三,三,四,三,五,小调六,六,小调七,七,八度,小调九,九,十,十一,大调七,十三
一个好的复制品也可能是Enum.GetNames
。我希望上述组的名称总是在它们的值匹配名称之前。
我基本上是在寻找每个值的枚举名称的优先级/优先级规则的文档。
您可以使用下面的代码:http://rextester.com/EJOWK87857.
我现在正在查看反编译的Enum.GetNames
。看起来它使用了反射。那么问题来了,"如何控制反射字段的顺序?"
如果不使用元数据,这是不可能的,因为编译器可能会将常量值赋给每个enum成员。检查编译后的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;
}