Switch + Enum = Не все пути кода возвращают значение
Мне просто интересно, почему этот код...
enum Tile { Empty, White, Black };
private string TileToString(Tile t)
{
switch (t)
{
case Tile.Empty:
return ".";
case Tile.White:
return "W";
case Tile.Black:
return "B";
}
}
Выдает эту ошибку. Невозможно ли t
взять любое другое значение, не так ли? Должен ли компилятор быть достаточно умным, чтобы понять это?
Ответы
Ответ 1
Нет, вы можете использовать любое значение int
, преобразованное в Tile
. Попробуйте следующее:
Tile t = (Tile) 5;
string s = TileToString(t);
Перечисление - это набор имен для чисел, эффективно... но ни компилятор, ни CLR не принуждают, что значение типа перечисления имеет имя. Это боль, но там она...
Я бы предложил случай по умолчанию, который бросил ArgumentException
(или, возможно, ArgumentOutOfRangeException
).
Ответ 2
Джон, конечно, совершенно прав, что перечисление может иметь любое значение своего базового типа, поэтому коммутатор не является исчерпывающим, и поэтому существует путь к коду, который не возвращается. Однако это не полный анализ проблемы. Даже если бы это было так, что коммутатор был исчерпывающим, вы все равно получили бы ошибку.
Попробуйте:
int M(bool b)
{
switch(b)
{
case true : return 123;
case false: return 456;
}
}
или
int M(byte b)
{
switch(b)
{
case 0: case 1: case 2: ... all of them ... case 255: return 123;
}
}
В обоих случаях вы получите ту же ошибку "достижимая конечная точка в не-void-методе".
Это просто надзор в разделе "Проверка доступности" в спецификации С#. Мы определяем конечную точку оператора switch как достижимую, если в ней нет раздела по умолчанию, периода. Для коммутаторов нет специального разрешения, которое исчерпывает все возможные значения их ввода. Это угловой случай, который разработчики языка пропустили, и он никогда не был достаточно высоким приоритетом для его исправления.
Для трех других интересных фактов об анализе операторов switch см.:
http://ericlippert.com/2009/08/13/four-switch-oddities/
Ответ 3
Это связано с тем, что если ваше значение для t не соответствует ни одному из случаев переключения, оно выпадет из коммутатора, и поэтому ваш метод не вернет значение. Однако вы заявили, что он вернет строку. Вам нужно добавить значение по умолчанию в коммутатор или вернуть null:
enum Tile { Empty, White, Black };
private string TileToString(Tile t)
{
switch (t)
{
case Tile.Empty:
return ".";
case Tile.White:
return "W";
case Tile.Black:
return "B";
}
return null;
}
Ответ 4
Добавьте пример по умолчанию:
enum Tile { Empty, White, Black };
private string TileToString(Tile t)
{
switch (t)
{
case Tile.Empty:
return ".";
case Tile.White:
return "W";
case Tile.Black:
return "B";
default:
return ".";
}
}
Ответ 5
switch (t)
{
case Tile.Empty:
return ".";
case Tile.White:
return "W";
case Tile.Black:
return "B";
default: throw new NotSupportedException();
}
Как указал Джон, значение интегрально - перечислить можно из любого целочисленного значения. Вам просто нужно обработать значение по умолчанию.