2012-01-31 2 views
7

У меня возникли трудности с работой с некоторыми устаревшими перечислениями, которые имеют несколько нулевых значений. Всякий раз, когда я вызываю ToString на одно из ненулевых значений, включается все, кроме первого нулевого значения.Получение имен элементов из перечислений с несколькими нулевыми значениями

Есть ли способ изолировать ненулевое значение без использования манипуляции или отражения строк?

//all of the following output "Nada, Zilch, One" 
Console.WriteLine(TestEnum.One); 
Console.WriteLine(Convert.ToString(TestEnum.One)); 
Console.WriteLine(TypeDescriptor.GetConverter(typeof(TestEnum)) 
         .ConvertToString(TestEnum.One)); 

[Flags] 
enum TestEnum 
{ 
    Zero = 0, 
    Nada = 0, 
    Zilch = 0, 
    One = 1 
} 

Редактировать

Я понимаю, что имея несколько элементов с одинаковым значением не рекомендуется, однако перечисление в вопросе определяется в унаследованной сборки, я не могу изменить. На самом деле, есть 12 открытых перечислений в mscorlib v4, которые нарушают эту рекомендацию, как определено по следующей простой LINQ запрос:

var types = typeof (void).Assembly.GetTypes() 
    .Where(type => type.IsEnum && 
        type.IsPublic && 
        Enum.GetValues(type).Cast<object>() 
         .GroupBy(value => value) 
         .Any(grp => grp.Count() > 1)) 
    .ToList(); 
+1

Это обычная практика, чтобы только один нулевое значение. На самом деле общепринятой практикой является отсутствие каких-либо повторяющихся значений. Это перечисление, и существует неотъемлемая опасность, полагающаяся на ценность «поддержки». –

+0

Можете ли вы показать код, как эти флаги используются? Используются ли они как бит-маски? – Maggie

+0

Поскольку это совершенно нежелательное поведение, вы можете отправить отчет об ошибке: http://connect.microsoft.com. – Heinzi

ответ

1

Вот один вариант. Это работает, но это немного уродливо. Переменные values ​​/ names не будут меняться, поэтому их нужно будет только вычислить.

Предполагая, что вы немного более сложным, перечисление, например:

[Flags] 
public enum TestEnum 
{ 
    Zero = 0, 
    Nada = 0, 
    Zilch = 0, 
    One = 1, 
    Two = 2, 
    Three = 3, 
    Four = 4 
} 

Вот код, вы можете использовать:

var input = TestEnum.One | TestEnum.Two; 
var values = (TestEnum[]) Enum.GetValues(typeof (TestEnum)); 
var names = Enum.GetNames(typeof (TestEnum)); 
var result = values 
    .Select((value, index) => 
      input == value || (value != 0 && (input & value) == value) 
       ? names[index] 
       : null) 
    .Where(name => name != null); 
var text = string.Join(", ", result); 
Console.WriteLine(text); 
+0

Я сделал ваш код LINQ более идиоматичным, надеюсь, вы не против. Мне нравится это решение, однако оно не работает для 0 значений. –

+0

Обновлено - теперь оно работает для 0 значений. – CodeThug

1

Хорошо, первые Microsoft recommends against this strongly. Некоторые из более сильных слов, которые я слышал, как они используют что-то они не выполняют на компиляции: установка

Избегайте значение перечисления флагов к нулю, если значение не используются для указания того, что все флаги будут удалены. Такое значение должно быть названо соответствующим образом, как описано в следующем руководстве ... Назовите нулевое значение перечислений флагов None. Для перечисления флагов значение всегда должно означать, что все флаги очищаются.

Хорошо, так почему это происходит? Из this question я беру это Enum.ToString себя странно:

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

EDIT: Я могу воспроизвести ваши результаты, но я не могу найти больше документации о том, почему он начнет печатать другие значения 0. Я ожидаю, что он напечатает НИЖЕ из них.

Можете ли вы просто щелкнуть правой кнопкой мыши-> refactor-> переименовать их все равно, а затем удалить остальные? Кажется, все проще и меньше, чем рекомендует Microsoft.

+0

Это устаревшее перечисление, определенное в сборке, которую я не могу изменить. –

+0

Почему вы против отражения? Любая конкретная причина? Это определенно выполнимо с этим. – DanTheMan

+0

Я не совсем против, но я предпочел бы, чтобы он использовался только в качестве крайней меры. Кроме того, мой код может потребоваться выполнить в частичной среде доверия. –

0
Enum.GetValues(typeof (TestEnum)) 

возвращает

{ClassLibrary5.Class1.TestEnum[4]} 
[0] Zilch ClassLibrary5.Class1.TestEnum 
[1] Zilch ClassLibrary5.Class1.TestEnum 
[2] Zilch ClassLibrary5.Class1.TestEnum 
[3] One ClassLibrary5.Class1.TestEnum 

Я собирался процитировать тот же отрывок Дэн сделал. Для чего они предназначены? Что вам нужно сделать с этими перечислениями?

0

Если у вас есть немного более сложное перечисление, например:

[Flags] 
public enum TestEnum 
{ 
    Zero = 0, 
    Nada = 0, 
    Zilch = 0, 
    One = 1, 
    Two = 2, 
    Four = 4, 
}

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

public static string TestEnumToString(TestEnum value) 
{ 
    var result = new List(); 

    if (value == TestEnum.Zero) 
    { 
     result.Add("Zero"); 
    } 
    if (value == TestEnum.Nada) 
    { 
     result.Add("Nada"); 
    } 
    if (value == TestEnum.Zilch) 
    { 
     result.Add("Zilch"); 
    } 
    if ((value & TestEnum.One) != 0) 
    { 
     result.Add("One"); 
    } 
    if ((value & TestEnum.Two) != 0) 
    { 
     result.Add("Two"); 
    } 
    if ((value & TestEnum.Four) != 0) 
    { 
     result.Add("Four"); 
    } 

    return string.Join(",", result); 
}
+1

Пример здесь не будет работать, так как это отмечено как «флаги». Если у меня есть 0x03 в качестве моего значения, но только 0x01 и 0x02 в моем перечислении, это вызовет исключение AgumentException. – DanTheMan

+0

Обновлен код для атрибута Flags. Пожалуйста ознакомтесь. – CodeThug

+0

Это решение создает нагрузку на обслуживание будущих разработчиков. Я предпочитаю ваше другое предложение. –

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