2013-05-12 4 views
3

Учитывая следующее перечисление:Внеочередные имен перечислений

[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, AugmentedThi го, AugmentedThird, AugmentedThird, DoubleDiminishedSixth, DoubleDiminishedSixth и т.д.

В то время как я хочу, чтобы имена перечислений, выбранные для одинаковых значений, чтобы быть в следующей последовательности:

Root, MinorSecond, Во-вторых, MinorThird, В-третьих, В-четвертые, Triton, Пятый, MinorSixth, шестой, MinorSeventh, седьмой, октава, MinorNinth, девятый, десятый, одиннадцатый, MajorEleventh, Тринадцать

хорошее воспроизведение также будет Enum.GetNames. Я хочу, чтобы имена вышеуказанной группы всегда должны предшествовать именам сопоставления значений.

В основном я ищу документацию о правилах приоритета/приоритета имен перечислений на каждое значение.

Вы можете поиграть с кодом здесь: http://rextester.com/EJOWK87857.

Update

Я теперь, глядя в декомпилируемой Enum.GetNames. Похоже, он использует отражение. Поэтому возникает вопрос: «Как контролировать порядок отраженных полей?».

+0

AFAIK, это не мя возможно. Например, метод [Enum.GetName] (http://msdn.microsoft.com/en-us/library/system.enum.getname.aspx) специально указывает, что вы не должны зависеть от последовательных результатов при поиске имени перечислений с повторяющимися значениями. Вы можете использовать 'Enum.GetNames' (который возвращает значения, как вы хотите), и используя эти имена, чтобы затем получить значения в порядке с [Enum.Parse] (http://msdn.microsoft.com/en-us /library/essfb559%28v=vs.90%29.aspx) method.EDIT: Но результаты 'Parse' будут по-прежнему повторяться, а не оригиналы; не уверен, что это помогает. –

+0

@ChrisSinclair Теперь я изучаю декомпилированные 'Enum.GetNames'. Похоже, он использует отражение. Поэтому возникает вопрос: «Как контролировать порядок отраженных полей?». – Shimmy

ответ

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; 
} 
+0

Я думаю, я удалю все дублированные значения и оставлю только «по умолчанию». Потому что я хочу, чтобы «ToString» возвращал эти значения. Атрибут ничего мне не даст. – Shimmy

+0

Почему бы не создать два отдельных перечисления, один со всеми значениями и один (второй), только с элементами «по умолчанию». Вы можете использовать от первого до второго, а вызов 'ToString()' во втором перечислении будет показывать только значения по умолчанию. – drf

+0

Хорошая идея - плохая привычка дизайна. – Shimmy

Смежные вопросы