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 для большего контроля... но я думаю, что для этого нужно много шаблонов код, который нелегко сделать общим.