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

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

switch ( myObj.GetType ( ) )
{
    case typeof(MyObject):
        Console.WriteLine ( "MyObject is here" );
        break;
}
10.11.2009 20:34:58
Brother Erryn 10.11.2009 20:39:33
@RHSeeger: В блоге Петтера Халлама не было никакого заявления, которое можно было бы истолковать как «люди слишком глупы, чтобы понять концепцию», а также о том, что разработчики языка проигнорировали это, потому что в мире есть глупые люди.
jason 10.11.2009 20:57:54
Это не имеет ничего общего с людьми, которые "слишком глупы". По мнению Питера, люди находят это поведение удивительным , а не непостижимым . C # был тщательно разработан, чтобы не удивлять, когда это возможно.
Eric Lippert 10.11.2009 21:19:53
Если у вас когда-нибудь возникает желание написать switchоператор, который включает тип объекта, вам действительно необходимо провести рефакторинг и делегировать эти случаи реализациям объекта. В хорошо спроектированной ОО-системе никогда не должно быть необходимости делать это.
Daniel Pryden 10.11.2009 21:19:56
Помните, что переключение на тип объекта - сильный запах кода. Включение типа для выяснения того, какой код выполнять / какой метод вызывать, это как сказать: «Я бы хотел, чтобы у всех моих объектов, с которыми я здесь столкнулся, был метод, который я мог бы вызвать прямо сейчас». Поэтому лучше задать вопрос: «Почему я должен включать тип объекта?» и когда вы ответите на этот вопрос, исправьте проблему вместо симптома :)
Rune FS 10.11.2009 21:32:13
10 ОТВЕТОВ
РЕШЕНИЕ

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

Из §8.7.2:

switch-label:
   case   constant-expression   :
   default   :

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

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

13
23.05.2017 11:54:59
Спасибо Марк. Если они обрабатывают строки, используя словарь, почему MS не делает то же самое для других типов, используя словарь?
Joan Venge 10.11.2009 20:49:48
vb.net позволяет все, что угодно, однако
Pondidum 10.11.2009 21:23:08
@joan, потому что он работает только для констант времени компиляции (таких как строковые литералы). Ключ используется в коде IL и, следовательно, должен быть известен во время компиляции
Rune FS 10.11.2009 21:29:24
@Pondidum - я ожидаю, что коммутатор VB.Net просто реализует серию If в IL. Если вы делаете шаг за шагом, видите ли вы код остановки в каждом «случае» вместо того, чтобы сразу же перейти к правильному?
Phil Whittington 26.01.2015 10:22:41
@RuneFS Выражения в форме typeof(SomeType)также постоянны, но не допускаются. Возможно typeof(T)или typeof(SomeType<T>)(дженерики) все еще будут запрещены, потому что нет способа проверить уникальность во время компиляции, но если не считать этого, любой тип должен быть законным.
Mr. TA 23.09.2015 16:55:09

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

2
10.11.2009 20:37:41

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

2
10.11.2009 20:43:25

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

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

5
10.11.2009 20:41:09
За исключением того, что уникальность может быть определена для typeof()выражений просто отлично (за исключением обобщений), тогда порядок не имеет значения.
Mr. TA 23.09.2015 16:59:18

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

1
10.11.2009 20:45:32
@PhilipWallace, что ответ Марка имеет отношение к ToString()подходу?
Mr. TA 23.09.2015 17:01:22

Второй на посту Питера Халлама; это отличное объяснение.

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

switch (Type.GetTypeCode(myObj.GetType())) {
    case TypeCode.Boolean: ...
    case TypeCode.Char: ...
    case TypeCode.String: ...
    case TypeCode.Object: ...
    default: ...
} 
19
10.11.2009 20:47:43
Любая идея, как проверить на Emums?
d.popov 15.09.2015 11:57:54

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

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

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

4
10.11.2009 20:48:35
Как это может работать, потому что значение еще не известно во время компиляции!
Philip Wallace 10.11.2009 20:54:54
@Philip: Эти caseзначения являются известны во время компиляции, потому что они зашиты как строки в этом случае. Это не оптимальное решение, но оно работает.
Daniel Pryden 10.11.2009 21:21:20

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

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

15
10.11.2009 21:15:45
Спасибо Эрик, хороший ответ. Кто-то здесь сказал, что vb.net может сделать это. Как это работает? Это компромисс по производительности vb.net или просто другая конструкция, чем оператор C # switch?
Joan Venge 10.11.2009 22:38:16
Документация для конструкции VB находится здесь: msdn.microsoft.com/en-us/library/cy37t14y.aspx
Eric Lippert 10.11.2009 23:11:29
Спасибо, Эрик, просто предположил, что постер здесь правильный. Теперь я это понимаю.
Joan Venge 10.11.2009 23:55:34
По ссылке выше , для testexpression , документация для конструкции VB гласит: «Обязательный. Выражение. Должен вычислять один из элементарных типов данных ( Boolean , Byte , Char , Date , Double , Decimal , Integer , Long , Object , SByte». , Short , Single , Строка , UInteger , ULong и UShort ) «. Так что, как и C #, VB.NET ничего не позволяетв качестве тестового выражения для оператора Select ... Case .
DavidRR 18.03.2014 12:43:59
«Заданное значение обычно имеет много типов» - это не имеет никакого смысла вообще. Каждое значение в .NET имеет только один тип. То, от чего этот тип наследует, является базовым классом, имеет в качестве обобщенных аргументов, совершенно не имеет значения.
Mr. TA 23.09.2015 16:57:42

У MS нет веской причины не реализовывать переключение типов, кроме лени.

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

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

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

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

-1
9.03.2012 02:20:17

В C # 7.0 вы можете это сделать. Смотрите Pattern Matching в C # 7.0 Case Blocks

// ----- 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;
}
1
21.05.2017 19:31:08