Почему оператор С# switch не позволяет использовать typeof/GetType()?

Как в этом примере:

switch ( myObj.GetType ( ) )
{
    case typeof(MyObject):
        Console.WriteLine ( "MyObject is here" );
        break;
}

Ответ 1

Проблема заключается в том, что switch (по спецификации) работает только с примитивами (int и т.д.) и строками. Но да, было бы неплохо иметь соответствие стиля F #.

Из §8.7.2:

switch-label:
   case   constant-expression   :
   default   :

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

Очевидно, однако, что работа с таким ограниченным множеством допускает простой (и эффективный) IL. Обратите внимание, что string обрабатывается с помощью карты словаря целым числом.

Ответ 2

Во-вторых, пост Питера Халлама; это отличное объяснение.

Вы можете использовать TypeCode для работы с простыми типами.

switch (Type.GetTypeCode(myObj.GetType())) {
    case TypeCode.Boolean: ...
    case TypeCode.Char: ...
    case TypeCode.String: ...
    case TypeCode.Object: ...
    default: ...
} 

Ответ 3

Я бы добавил к Питеру отличный анализ следующей мысли:

В принципе, целью "переключателя" является выбор одного из нескольких различных возможностей. Значение переменной enum, integer, Boolean или string может быть только одним значением, поэтому имеет смысл "переключать" на такое значение. Но типы принципиально разные. Данное значение обычно имеет много типов. Типы часто перекрываются. Предлагаемый "переключатель типа" не соответствует заявленной цели конструкции коммутатора.

Ответ 4

Там хорошая запись в блоге на MSDN Peter Hallam, которая объясняет проблемы включения непостоянных значений.

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

Ответ 5

Вы могли бы сделать

switch ( myObj.GetType().Name )
{
    case "MyObject":
        Console.WriteLine ( "MyObject is here" );
        break;
}

Это работает, потому что переключение работает только на примитивные типы (как говорили другие).

Ответ 6

Это то, что typeof не является константой, а случаи должны быть константами.

Ответ 7

переключатель в С# работает только для интегралов или строк. myObj.GetType() возвращает тип, который не является ни интегралом, ни строкой.

Ответ 8

Почему бы вам просто не tostring()?

Ответ 9

В С# 7.0 вы можете это сделать. Видеть Соответствие шаблону в корпусах Case С# 7.0

// ----- Assume that spaceItem is of type SpaceType,
//       and that Planet and Star derive from SpaceType.
switch (spaceItem)
{
  case Planet p:
    if (p.Type != PlanetType.GasGiant)
      LandSpacecraft(p);
    break;
  case Star s:
    AvoidHeatSource(s);
    break;
  case null:
    // ----- If spaceItem is null, processing falls here,
    //       even if it is a Planet or Star null instance.
    break;
  default:
    // ----- Anything else that is not Planet, Star, or null.
    break;
}

Ответ 10

Нет никаких оснований для того, чтобы MS не применяла типы включения, кроме лени.

Переключение строк выполняется с помощью "if (.. Equals (..))" с несколькими случаями и Словарем со многими случаями. Оба этих подхода определены для всех типов .NET, потому что System.Object имеет Equals и GetHashCode, которые являются виртуальными.

Можно сказать, что "switch может использовать выражение любого типа, где Equals и GetHashCode переопределены", который автоматически квалифицирует строку, тип и т.д. Да, неудачная реализация Equals/GetHashCode нарушит оператор switch, но, эй, вы может также разорвать оператор "==", цикл "foreach" и множество других вещей, поэтому я действительно не вижу "большой проблемы" с отключением коммутатора ошибкой программиста. Но даже если они не хотят разрешать его для всех типов, по какой-либо причине, конечно, тип безопасен, потому что Type.Equals() корректно определен и GetHashCode также реализован.

Кроме того, я не покупаю аргумент, который вы рассматривали наследование; switch переходит к случаю, чья константа (и тип (int) является константой, не ошибитесь в этом) равна выражению - наследование - это другое "поведение" типа Type. Не нужно даже рассматривать наследование, я имею в виду, мы отказываемся сравнивать 2 объекта только потому, что у них есть другие качества? Нет, мы этого не делаем, потому что равенство всегда определено. В принципе, точка в том, что между разными типами нет перекрытия.

Итак, как я уже сказал, есть одна причина и одна причина: лень.:)