Есть ли способ заставить компилятор С# испускать ошибку, если у коммутатора (enum_val) отсутствует оператор case?
Я только что понял, что добавил значение в список значений "must-handle" в моем перечислении, но я не заметил его до выполнения. Я знаю, что компилятор С# действительно эффективен, когда дело доходит до размышлений и самоанализа типов, поэтому мне было интересно, есть ли способ заставить оператор switch
/case
покрыть все возможные значения enum
?
Пример:
enum Colors
{
Red,
Blue,
Green,
Yellow
};
Colors c = ...;
switch (c)
{
case Colors.Red: // No error, Red is a Color
break;
case Colors.Blue:
case Colors.Green: // No error, Blue and Green handled as well
break;
} // whoops! "error: 'Colors.Yellow' unhandled"
// or even, "error: no 'default' and 'Colors.Yellow' unhandled"
Я хочу решение для компиляции.
Ответы
Ответ 1
Просто, выбросив (каламбур непредвиденный), вы можете заменить Switch-Case на словарь (Func<int,int>
в качестве примера):
Dictionary<Colors,Func<int,int>> d = new Dictionary<Colors, Func<int, int>>();
d.Add(Colors.Red, (x) => x+1);
d.Add(Colors.Blue, (x) => x+1);
d.Add(Colors.Green, (x) => x+1);
foreach (Colors color in Enum.GetValues(typeof(Colors)))
{
if (!d.ContainsKey(color))
{
throw new Exception("Poor color " + color + " ignored");
}
}
Ответ 2
Нет никакого способа компиляции, чтобы это произошло. Однако очень ОЧЕНЬ простой ответ заключается в том, чтобы иметь обработчик default
, который просто генерирует исключение по строкам, "эта опция не обрабатывалась, boo".
switch (c)
{
case Colors.Red: // no error, Red is a Color
break;
case Colors.Blue:
case Colors.Green: // no error, Blue and Green handled as well
break;
default:
throw new Exception("Unhandled option: " + c.ToString());
}
Ответ 3
Вы можете использовать SwitchEnumAnalyzer, чтобы получать предупреждения о компиляторе такого типа. Я просто использовал его в своем проекте, и он работает хорошо. Как и в любом анализаторе Roslyn, вы можете выбрать уровень уведомления - если это должно быть просто предупреждение или правильная ошибка.
Ответ 4
Это лучший пример того, почему нам нужно тестирование во всех решениях. Вы должны написать тест, который может перечислить ваш Enum и вызвать этот метод, который содержит случай переключения. С помощью этого теста вы получите неудачный тест каждый раз, когда вы редактируете Enum, но забудьте обновить корпус переключателя.
[Test]
public void CheckThatAllEnumCoveredInSwitchCase()
{
foreach (var singleEnum in Enum.GetValues(typeof(YOURENUM)))
{
myclass.methodofswitchcase((YOURENUM)singleEnum);
}
}
Ответ 5
Вы можете выслать исключение на раннем этапе, если размер перечисления, надеюсь, вас предупредит очень рано, когда будет сделано изменение в перечисление:
enum MyEnum {A, B};
class TestEnum
{
// Static constructor
static TestEnum()
{
// Check if this code needs updating as the enum has changed
if (Enum.GetNames(typeof(MyEnum)).Length != 2)
{
// If this fails update myFunction and the number above
throw new Exception("Internal error - code inconsistency");
}
}
// My function that switches on the enum
string myFunction(MyEnum myEnum)
{
switch (myEnum)
{
case MyEnum.A: return "A";
case MyEnum.B: return "B";
}
throw new Exception("Internal error - missing case");
}
}
Это приведет к исключению из статического конструктора, если количество элементов в перечислении будет изменено. Поэтому разработчик знает, что ему нужно обновить код. Вы даже можете сделать эту проверку с unit test, который вы запускаете со своей сборкой.
Ответ 6
Вы можете использовать мета-метод, который проверяет во время выполнения, но, по крайней мере, проверяет весь коммутатор.
https://github.com/faisalmansoor/MiscUtil/blob/master/EnumSwitch/EnumSwitch.cs
Ответ 7
Для С++ вы можете заставить компилятор выдать предупреждение, добавив в список параметров компилятора /we 4061. Надеюсь это поможет.:)
http://msdn.microsoft.com/en-us/library/thxezb7y.aspx
http://msdn.microsoft.com/en-us/library/96f5t7fy (v = vs .80).aspx
На самом деле я ошибся в этом; похоже, вы можете заставить компилятор выдать ошибку. Просто скомпилируйте /Wall/we 4061. Я не пробовал это сам, но, читая приведенные выше страницы MSDN, похоже, что это должно работать.