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();
}

Как указал Джон, значение интегрально - перечислить можно из любого целочисленного значения. Вам просто нужно обработать значение по умолчанию.