Нечеткое поведение коммутатора в .NET 4

У меня возникла проблема с пониманием того, что вызывает ошибку компиляции в коде ниже:

static class Program
{
    static void Main()
    {
        dynamic x = "";
        var test = foo(x);

        if (test == "test")
        {
            Console.WriteLine(test);
        }

        switch (test)
        {
            case "test":
                Console.WriteLine(test);
                break;
        }
    }

    private static string foo(object item)
    {
        return "bar";
    }
}

Ошибка, которую я получаю, находится в строке switch (test):

A switch expression or case label must be a bool, char, string, integral, 
enum, or corresponding nullable type.

Intellisence показывает мне, что операция foo будет разрешена во время выполнения, что отлично, потому что я использую динамический тип в качестве параметра. Однако я не понимаю, как условие if компилируется нормально, когда переключатель не работает.

Код выше - это просто упрощенная версия того, что у меня есть в приложении (VSTO), которое появилось после переноса приложения из VSTO3 в VSTO4, когда один метод в VSTO был изменен, чтобы возвращать значения типа dynamic вместо object.

Может ли кто-нибудь дать мне объяснение, в чем проблема. Я знаю, как это решить, но я хотел бы понять, что происходит.

Ответ 1

Поскольку вы вызываете метод динамически, результатом также является dynamic (поскольку возвращаемое значение может быть любым - оно не известно до выполнения). И вы не можете switch по типу переменной dynamic.

Ответ 2

Тип выражения switch вычисляется компилятором во время компиляции. Тип dynamic оценивается во время выполнения, поэтому компилятор не может проверить, является ли он (или конвертируется) одним из разрешенных типов (который, согласно Спецификация языка С# 4 - это sbyte, byte, short, ushort, int, uint, long, ulong, bool, char, string или enum-type).

Ответ 3

Как говорит Мэтт Эллен, но немного больше фона.

Для оператора switch: из спецификации языка С# v4.0:

Тип управляющий тип оператора switch устанавливается выражением switch.

  • Если тип выражения switch sbyte, byte, short, ushort, int, uint, long, ulong, bool, char, string или enum-type, или если это тип с нулевым значением, соответствующий одному из этих типов, то это управляющий тип оператора switch.
  • В противном случае из типа выражения коммутатора должно существовать только одно определяемое пользователем неявное преобразование (§6.4): sbyte, byte, short, ushort, int, uint, long, ulong, char, string или тип с нулевым значением, соответствующий одному из этих типов.
  • В противном случае, если такое неявное преобразование не существует или существует более одного такого неявного преобразования, возникает ошибка времени компиляции.

Для оператора if выражение оценивается как операция Boolean. Оценка выражения отложена на время выполнения из-за использования dynamic в вызове метода в присваивании переменной. Из вышеприведенной спецификации выглядит, что switch требует оценки времени переключения типов коммутатора.

Ответ 4

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

Если вы замените:

var test = foo (x);

с:

string test = foo (x);

все компилируется, как вы знаете.

Это безопасно, поскольку вы объявили foo() как возвращающую строку и, возможно, немного более интуитивно понятным для чтения в долгосрочной перспективе.

Ответ 5

Операторы switch поддерживают только номера, символы, перечисления и строки. dynamic - ничто из этого. Если вы считаете, что x - это строка, вы можете просто ее перевести:

dynamic x = ""; 
string test = (string)foo(x); 

Вы просто получите ошибку времени выполнения, если это не так.