GetName для перечисления с повторяющимися значениями
Если у меня есть повторяющиеся значения в перечислении С#, говоря
enum MyE {
value1 = 1,
value2 = 2,
valued = 1
}
Какими должны быть значения следующих строк?
MyE N = (MyE)1;
string V1 = N.ToString();
string V2 = GetName(MyE, 1);
Верно ли, что V1 и V2 должны содержать одинаковые значения?
Какими должны быть эти значения?
Я ничего не нашел в MSDN или здесь относительно такого "разыменования" перечислений с дубликатами, укажите мне ссылку, пожалуйста, если я пропустил это.
Ответы
Ответ 1
Эксперимент показывает, что:
V1 = "value1"
и
V2 = "value1"
Однако это не гарантируется. Страница MSDN на Enum.GetName
гласит:
Если несколько элементов перечисления имеют одно и то же базовое значение, метод GetName гарантирует, что он вернет имя одного из этих элементов перечисления. Однако он не гарантирует, что он всегда будет возвращать имя того же элемента перечисления. В результате, когда несколько элементов перечисления имеют одинаковое значение, ваш код приложения никогда не должен зависеть от метода, возвращающего определенное имя участника.
Ответ 2
В разделе "Примечания" в документации по методу Enum.GetName
(http://msdn.microsoft.com/en-us/library/system.enum.getname.aspx) говорится:
Если несколько элементов перечисления имеют одно и то же базовое значение, метод GetName гарантирует, что он вернет имя одного из этих элементов перечисления. Однако он не гарантирует, что он всегда будет возвращать имя того же элемента перечисления. В результате, когда несколько элементов перечисления имеют одинаковое значение, ваш код приложения никогда не должен зависеть от метода, возвращающего определенное имя участника.
Я проверил тест, чтобы увидеть, что будет происходить экспериментально, и оно всегда возвращает первое значение, определенное (в вашем примере, значение1), но, согласно официальной документации выше, вы не можете полагаться на это (см. комментарий @gluk47, что указывает на различное поведение в дикой природе).
Ответ 3
Я не согласен с другими утверждениями ответов
... это не гарантируется...
... вы не можете полагаться на это...
а также с инструкцией 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 }
Это позволяет десериализовать значение старого enum (сделанное более старыми версиями программного обеспечения) как AAA
.
Затем появился отчет, который печатает значение Blabla
. И в какой-то момент каждый клиент, использующий новую версию, начинает рассказывать мне, что вместо AAA
они видят AA
значение. Все они видят AA
(и ни один отчет не видит A
).
Что я сделал? Я просто меняю порядок (пока результат не был AAA
)
enum Blabla { AAA = 0, A = 0, AA = 0, ...}
и сделал тест для обеспечения того, что Blabla.AAA
будет выводиться как AAA
. Проблема решена?
Доказательство
Рассматривая источники Enum.ToString()
(или Enum.GetName()
), он использует GetEnumName(), который вызывает Array.BinarySearch()
для отсортированного массива значений, чтобы найти индекс значения.
Результат двоичного поиска детерминирован: предоставление ему тех же параметров возвращает тот же результат.
Итак:
- Если вы не измените enum, тогда результат будет таким же.
- можно найти результат экспериментально (или, возможно, понять, как работает бинарный поиск и как будет обрабатываться enum).
- нет простого правила для определения результата (например, вы не можете сказать "он всегда возвращает первое определенное значение" ).
- маловероятно, что формат
enum
будет изменен (например, порядок определения и порядок отображения значений отличается) или Enum.ToString()
будет изменен, однако это может произойти, поэтому убедитесь, что у вас есть тесты для случаев, когда вы полагаетесь на возвращаемое значение.
Ответ 4
Хорошо, потому что нет никаких гарантий относительно порядка, и устаревший атрибут, похоже, не влияет на методы Enum
, я обычно предлагаю использовать свойство string для целей сериализации и использовать собственный код для обработки устаревших значений.
Таким образом, первичный Enum
не имеет дубликатов и содержит только текущие значения.
В зависимости от структуры сериализации вы можете пометить свойство строки как сериализуемое, но устаревшее, а фактическое свойство, используемое в коде, будет проигнорировано сериализацией.
Чтобы обрабатывать устаревшие значения в настройке свойств строки, вы можете использовать оператор [Obsolete] enum
или switch. Как правило, я бы, вероятно, использовал бывшую, если у меня много устаревших значений для обработки, а последнее, если у меня есть только несколько значений для обработки.
Также имеет смысл поставить код в вспомогательный класс или метод расширения, особенно если вы загружаете или сохраняете это значение из нескольких мест.
Хотя я не очень разбирался в производственном коде, мне интересно, лучше ли использовать struct
вместо Enum
для большего контроля... но я думаю, что для этого нужно много шаблонов код, который нелегко сделать общим.