Ограничения оператора переключения C# - почему?

В Ubuntu в начале 2018 года нет python3.6-tk в нормальных дистрибутивах ubuntu (xenial / 16.04), поэтому даже если у вас более ранние версии python-tk, это не сработает.

My решение должно было использовать все с помощью python 3.5:

 sudo apt install python3.5-tk
 virtualenv --python=`which python3.5` python-env
 source python-env/bin/activate
 pip install -r requirements.txt

И теперь matplotlib может найти tkinter.

EDIT:

I просто нужно было 3.6 afterall, а трюк был:

sudo apt install tk-dev

, а затем перестроить python3.6, после tk-dev, например:

./configure
make
make install
136
задан Community 17 December 2014 в 19:27
поделиться

15 ответов

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

оператор переключения не является тем же самым как большим если еще оператор. Каждый случай должен быть уникальным и оценен статически. Оператор переключения делает постоянное ответвление времени независимо от того, сколько случаев Вы имеете. Если еще оператор оценивает каждое условие, пока это не находит тот, который верен.

<час>

На самом деле, оператор переключения C# не всегда постоянное ответвление времени.

В некоторых случаях компилятор будет использовать оператор переключения CIL, который является действительно постоянным ответвлением времени с помощью таблицы переходов. Однако в редких случаях, как указано Ivan Hamilton компилятор может генерировать что-то еще полностью.

Это на самом деле довольно легко проверить путем записи различных операторов переключения C#, некоторые редкие, некоторые плотные, и рассмотрения получающегося CIL с инструментом ildasm.exe.

97
ответ дан 23 November 2019 в 23:39
поделиться

У меня нет фактически знания C#, но я подозреваю, что или переключатель был просто взят, как это происходит на других языках, не думая о создании, он более общий или разработчик решил, что расширение его не стоило того.

Строго говоря Вы абсолютно правы, что нет никакой причины поместить эти ограничения на него. Можно было бы подозревать, что причина состоит в том, что для позволенных случаев внедрение очень эффективно (как предложил Brian Ensink ( 44921 )), но я сомневаюсь, что внедрение очень эффективно (w.r.t. операторы "if"), если я использую целые числа и некоторые случайные случаи (например, 345,-4574 и 1234203). И в любом случае, что является вредом в разрешении его для всего (или по крайней мере больше) и говорящий, что это только эффективно для конкретных случаев (такой как (почти) последовательные числа).

я могу, однако, предположить, что можно было бы хотеть исключить типы из-за причин такой как один данный lomaxx ( 44918 ).

Редактирование: @Henk ( 44970 ): Если Строки будут максимально совместно использованы, то строки с равным содержанием будут указателями на ту же ячейку памяти также. Затем если можно удостовериться, что строки, используемые в случаях, хранятся последовательно в памяти, можно очень эффективно реализовать переключатель (т.е. с выполнением в порядке 2 выдерживает сравнение, дополнение и два перехода).

0
ответ дан 23 November 2019 в 23:39
поделиться

Я соглашаюсь с этот комментарий , что использование табличного подхода часто лучше.

В C# 1.0 это не было возможно, потому что он не имел дженериков и анонимных делегатов. Новые версии C# имеют леса для создания этой работы. Наличие нотации для литералов объектов, также помогает.

0
ответ дан 23 November 2019 в 23:39
поделиться

Я думаю, что Henk закрепил его с "никаким статическим доступом к системе типов" вещь

, Другая опция состоит в том, что нет никакого порядка к типам, где, поскольку численные данные и строки могут быть. Таким образом переключатель типа был бы, не может создать дерево двоичного поиска, просто линейный поиск.

0
ответ дан 23 November 2019 в 23:39
поделиться

записал:

"Оператор переключения делает постоянное ответвление времени независимо от того, сколько случаев Вы имеете".

, Так как язык позволяет строка тип использоваться в операторе переключения, который я предполагаю, компилятор не может сгенерировать код для постоянной реализации ответвления времени для этого типа и должен генерировать если затем стиль.

@mweerden - А-ч я вижу. Спасибо.

у меня нет большого опыта в C# и.NET, но кажется, что разработчики языка не предоставляют статический доступ к системе типов кроме узких обстоятельств. ключевое слово typeof возвращает объект, таким образом, это доступно во времени выполнения только.

0
ответ дан 23 November 2019 в 23:39
поделиться

Согласно документация оператора переключения , если будет однозначный способ неявно преобразовать объект в целочисленный тип, то это будет позволено. Я думаю, что Вы ожидаете поведение, где для каждого оператора выбора оно было бы заменено if (t == typeof(int)), но это откроет целую кучу проблем, когда Вы доберетесь для перегрузки того оператора. Поведение изменилось бы, когда реализация детализирует для оператора переключения, измененного, если Вы записали Ваш == переопределение неправильно. Путем сокращения сравнений с целочисленными типами и строкой и теми вещами, которые могут быть уменьшены до целочисленных типов (и предназначаются к) они избегают потенциальных проблем.

0
ответ дан 23 November 2019 в 23:39
поделиться

Это не причина, почему, но спецификация C# разделяют 8.7.2 состояний следующее:

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

спецификация C# 3.0 расположена в: http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20Language%20Specification.doc

3
ответ дан 23 November 2019 в 23:39
поделиться

Я предполагаю, что нет никакой фундаментальной причины, почему компилятор не мог автоматически перевести Ваш оператор переключения в:

if (t == typeof(int))
{
...
}
elseif (t == typeof(string))
{
...
}
...

, Но нет очень получен этим.

оператор выбора А на целочисленных типах позволяет компилятору делать много оптимизации:

  1. нет никакого дублирования (если Вы не копируете маркировки случая, которые компилятор обнаруживает). В Вашем примере t мог соответствовать нескольким типам из-за наследования. Первое должно соответствовать быть выполненным? Все они?

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

0
ответ дан 23 November 2019 в 23:39
поделиться

В то время как по теме, по словам Jeff Atwood, оператор переключения является злодеянием программирования . Используйте их экономно.

можно часто выполнять ту же задачу с помощью таблицы. Например:

var table = new Dictionary<Type, string>()
{
   { typeof(int), "it's an int!" }
   { typeof(string), "it's a string!" }
};

Type someType = typeof(int);
Console.WriteLine(table[someType]);
6
ответ дан 23 November 2019 в 23:39
поделиться

Между прочим, VB, имея ту же базовую архитектуру, позволяет намного более гибкий Select Case операторы (вышеупомянутый код работал бы в VB), и все еще производит эффективный код, где это возможно, таким образом, аргумент techical ограничением нужно рассмотреть тщательно.

10
ответ дан 23 November 2019 в 23:39
поделиться

Главным образом те ограничения существуют из-за разработчиков языка. Базовое выравнивание может быть совместимостью с languange историей, идеалами или упрощением дизайна компилятора.

компилятор может (и делать), примите решение:

  • создают большое если еще использование оператора
  • инструкция по переключателю MSIL (таблица переходов)
  • сборка Дженерик. Dictionary< строка, int32> заполните его на первом использовании и Универсальном вызове. Dictionary< >:: TryGetValue (), чтобы индекс передал инструкции по переключателю MSIL (таблица переходов)
  • использование комбинация если-elses & переходы "переключателя" MSIL

оператор переключения НЕ ЯВЛЯЮТСЯ постоянным ответвлением времени. Компилятор может найти ярлыки (использующий блоки хеша, и т.д.), но более сложные случаи сгенерируют более сложный код MSIL с некоторыми случаями, расширяющимися ранее, чем другие.

Для обработки Строкового случая компилятор закончит тем (в какой-то момент), что использовал a. Равняется (b) (и возможно a. GetHashCode ()). Я думаю, что это был бы trival для компилятора для использования любого объекта, который удовлетворяет эти ограничения.

Что касается потребности в статических case-выражениях... некоторые из тех оптимизаций (хеширование, кэширование, и т.д.) не были бы доступны, если бы case-выражения не были детерминированы. Но мы уже видели, что иногда компилятор просто выбирает упрощенную if-else-if-else дорогу так или иначе...

Редактирование: lomaxx - Ваше понимание "typeof" оператора не корректно. "typeof" оператор используется для получения Системы. Текстовый объект для типа (ничто, чтобы сделать с его супертипами или интерфейсами). Проверка совместимости во время выполнения объекта с данным типом "," задание оператора. Использование "typeof" здесь для выражения объекта не важно.

10
ответ дан 23 November 2019 в 23:39
поделиться

Первая причина, которая приходит на ум, историческая :

Начиная с большей части C, C++ и программисты Java не приучены к наличию таких свобод, они не требуют их.

Другой, более допустимый, причина - то, что сложность языка увеличилась бы :

, В первую очередь, объекты должны быть по сравнению с .Equals() или с == оператор? Оба допустимы в некоторых случаях. Мы должны представить новый синтаксис, чтобы сделать это? Мы должны позволить программисту представлять их собственный метод сравнения?

, Кроме того, позволяя включать объекты был бы предположения лежания в основе повреждения об операторе переключения . Существует два правила, управляющие оператором переключения, который компилятор не смог бы осуществить, если бы объектам позволили быть включенными (см. спецификация языка версии 3.0 C#, В§8.7.2):

  • , Что значения маркировок переключателя постоянные
  • , Что значения маркировок переключателя отличны (так, чтобы только один блок переключателя мог быть выбран для данного выражения переключателя)

Рассматривают этот пример кода в гипотетическом случае, который были позволены непостоянные значения случая:

void DoIt()
{
    String foo = "bar";
    Switch(foo, foo);
}

void Switch(String val1, String val2)
{
    switch ("bar")
    {
        // The compiler will not know that val1 and val2 are not distinct
        case val1:
            // Is this case block selected?
            break;
        case val2:
            // Or this one?
            break;
        case "bar":
            // Or perhaps this one?
            break;
    }
}

, Что сделает код? Что, если операторы выбора переупорядочиваются? Действительно, одна из причин, почему C#, сделанный переключателем провалиться недопустимый, - то, что операторы переключения могли быть произвольно перестроены.

Эти правила существуют по причине - так, чтобы программист, путем рассмотрения одного блока случая, мог знать наверняка точное условие, при котором вводится блок. Когда вышеупомянутый оператор переключения превратится в 100 строк или больше (и он будет), такое знание неоценимо.

23
ответ дан 23 November 2019 в 23:39
поделиться

Важно не перепутать оператор переключения C# с инструкцией по переключателю CIL.

переключатель CIL является таблицей переходов, которая требует индекса в ряд адресов перехода.

Это только полезно, если случаи переключателя C# смежны:

case 3: blah; break;
case 4: blah; break;
case 5: blah; break;

, Но мало полезный, если они не:

case 10: blah; break;
case 200: blah; break;
case 3000: blah; break;

(Вам была бы нужна таблица ~3000 записи в размере только с 3 используемыми слотами)

С несмежными выражениями, компилятор может начать выполнять линейные if-else-if-else проверки.

С большим не - смежные наборы выражения, компилятор может запуститься с поиска двоичного дерева, и наконец if-else-if-else последние несколько объектов.

С наборами выражения, содержащими глыбы смежных объектов, компилятор может двоичное дерево искать, и наконец переключатель CIL.

Это полно "mays" & "силы", и это зависит от компилятора (может не согласиться с Моно или Ротором).

я копировал Ваши результаты на своей машине с помощью смежных случаев:

общее время для выполнения 10 путей переключатель, 10 000 повторений (мс): 25.1383
приблизительное время на 10 путей переключатель (мс): 0.00251383

общее время для выполнения 50 путей переключатель, 10 000 повторений (мс): 26.593
приблизительное время на 50 путей переключатель (мс): 0.0026593

общее время для выполнения 5 000 путей переключатель, 10 000 повторений (мс): 23.7094
приблизительное время на 5 000 путей переключатель (мс): 0.00237094

общее время для выполнения 50 000 путей переключатель, 10 000 повторений (мс): 20.0933
приблизительное время на 50 000 путей переключатель (мс): 0.00200933

Затем я также сделал использующие несмежные case-выражения:

общее время для выполнения 10 путей переключатель, 10 000 повторений (мс): 19.6189
приблизительное время на 10 путей переключатель (мс): 0.00196189

общее время для выполнения 500 путей переключатель, 10 000 повторений (мс): 19.1664
приблизительное время на 500 путей переключатель (мс): 0.00191664

общее время для выполнения 5 000 путей переключатель, 10 000 повторений (мс): 19.5871
приблизительное время на 5 000 путей переключатель (мс): 0.00195871

А несмежные 50 000 операторов переключения случая не скомпилировали бы.
"Выражение является слишком длинным или сложным для компиляции рядом 'ConsoleApplication1. Программа. Основной (строка [])'

то, Что забавно здесь, то, что поиск двоичного дерева кажется немного (вероятно, не статистически) более быстрым, чем инструкция по переключателю CIL.

Brian, Вы использовали слово" постоянный ", который имеет очень определенное значение с вычислительной точки зрения теории сложности. В то время как упрощенный смежный целочисленный пример может произвести CIL, который считают O (1) (постоянный), редким примером является O (зарегистрируйте n) (логарифмические), кластеризованные примеры лежат где-нибудь, промежуточными, и небольшими примерами является O (n) (линейный).

Это даже не обращается к Строковой ситуации, в которой помехи Generic.Dictionary<string,int32> могут быть созданы и перенесут определенные издержки на первом использовании. Производительность здесь будет зависеть от производительности Generic.Dictionary.

, Если Вы проверяете Спецификация языка C# (не спецификация CIL) Вы найдете "15.7.2, оператор переключения" не упоминает о "постоянном времени" или что конкретная реализация даже использует инструкцию по переключателю CIL (очень остерегаться принятия таких вещей).

В конце дня, переключатель C# против целого выражения в современной системе является операцией подмикросекунды, и не обычно стоящий волнения о.

<час>

, Конечно, эти времена будут зависеть от машин и условий. Я, о котором wouldn’t обращают внимание на эти тесты синхронизации, продолжительности микросекунды we’re говорящий, затмеваюсь любым выполняемым кодом “real” (и необходимо включать некоторый “real code” иначе, компилятор оптимизирует ответвление далеко), или дрожите в системе. Мои ответы основаны на использовании IL DASM для исследования CIL, созданного компилятором C#. Конечно, этот isn’t финал, как фактические инструкции выполнения ЦП затем создаются JIT.

я проверил заключительные инструкции ЦП, на самом деле выполняемые на моей x86 машине, и могу подтвердить простой смежный переключатель набора, делающий что-то как:

  jmp     ds:300025F0[eax*4]

, Где поиск двоичного дерева полон:

  cmp     ebx, 79Eh
  jg      3000352B
  cmp     ebx, 654h
  jg      300032BB
  …
  cmp     ebx, 0F82h
  jz      30005EEE
111
ответ дан 23 November 2019 в 23:39
поделиться

Я не вижу причин, по которым оператор switch должен соответствовать только статическому анализу

Верно, он не имеет , а многие языки делают фактически используйте операторы динамического переключения. Это означает, однако, что переупорядочение предложений case может изменить поведение кода.

За решениями по дизайну, которые вошли в "switch" здесь, есть некоторая интересная информация: Почему оператор switch C # разработан так, чтобы не разрешить провал, но все же требуется перерыв?

Разрешение динамических выражений case может привести к чудовищам, таким как этот код PHP:

switch (true) {
    case a == 5:
        ...
        break;
    case b == 10:
        ...
        break;
}

, который, откровенно говоря, должен просто использовать оператор if-else .

]
6
ответ дан 23 November 2019 в 23:39
поделиться

Ответ Джуды выше натолкнул меня на идею. Вы можете "сымитировать" поведение переключателя OP выше, используя Dictionary:

Dictionary<Type, Func<object, string,  string>> typeTable = new Dictionary<Type, Func<object, string, string>>();
typeTable.Add(typeof(int), (o, s) =>
                    {
                        return string.Format("{0}: {1}", s, o.ToString());
                    });

Это позволит вам связать поведение с типом в том же стиле, что и оператор switch. Я полагаю, что при компиляции в IL у него есть дополнительное преимущество: вместо таблицы переходов в стиле switch он будет иметь ключ.

3
ответ дан 23 November 2019 в 23:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: