Я недавно смотрел на F #, и, хотя я вряд ли скоро пройду через забор, он определенно выделяет некоторые области, где C # (или поддержка библиотеки) может облегчить жизнь.
В частности, я имею в виду возможность сопоставления с образцом в F #, которая допускает очень богатый синтаксис - гораздо более выразительный, чем нынешние переключатели / условные эквиваленты C #. Я не буду пытаться привести прямой пример (мой F # не подходит), но вкратце это позволяет:
- сопоставление по типу (с полной проверкой для различающихся объединений) [обратите внимание, что это также выводит тип для связанной переменной, предоставляя доступ к члену и т. д.]
- сопоставлять по предикату
- комбинации выше (и, возможно, некоторые другие сценарии, о которых я не знаю)
Хотя было бы неплохо, чтобы C # в конечном итоге заимствовал [гм] часть этого богатства, тем временем я смотрел на то, что можно сделать во время выполнения - например, довольно легко собрать некоторые объекты, чтобы позволить:
var getRentPrice = new Switch<Vehicle, int>()
.Case<Motorcycle>(bike => 100 + bike.Cylinders * 10) // "bike" here is typed as Motorcycle
.Case<Bicycle>(30) // returns a constant
.Case<Car>(car => car.EngineType == EngineType.Diesel, car => 220 + car.Doors * 20)
.Case<Car>(car => car.EngineType == EngineType.Gasoline, car => 200 + car.Doors * 20)
.ElseThrow(); // or could use a Default(...) terminator
где getRentPrice - это Func <Vehicle, int>.
[примечание - возможно Switch / Case здесь неправильные термины ... но это показывает идею]
Для меня это намного яснее, чем эквивалент с использованием многократного if / else или составного троичного условного выражения (которое становится очень грязным для нетривиальных выражений - скобки в изобилии). Это также позволяет избежать большого количества приведения и допускает простое расширение (либо напрямую, либо с помощью методов расширения) до более конкретных совпадений, например совпадение InRange (...), сравнимое с VB Select ... Case "x To y " использование.
Я просто пытаюсь оценить, если люди думают, что есть много преимуществ от конструкций, как указано выше (при отсутствии языковой поддержки)?
Обратите внимание, что я играл с 3 вариантами выше:
- версия Func <TSource, TValue> для оценки - сопоставимая с составными тройными условными выражениями
- версия Action <TSource> - сопоставимая с if / else if / else if / else if / else
- версия Expression <Func <TSource, TValue >> - как первая, но используемая произвольными поставщиками LINQ
Кроме того, использование версии на основе выражений позволяет переписывать дерево выражений, по существу объединяя все ветви в одно составное условное выражение, вместо использования повторного вызова. Я недавно не проверял, но в некоторых ранних сборках Entity Framework я вспоминаю, что это было необходимо, так как ему не очень нравилось InvocationExpression. Это также позволяет более эффективно использовать LINQ-to-Objects, поскольку позволяет избежать повторных вызовов делегатов - тесты показывают совпадение, аналогичное приведенному выше (с использованием формы выражения), с той же скоростью (на самом деле, немного быстрее) по сравнению с эквивалентным C # составное условное утверждение. Для полноты, основанная на Func <...> версия заняла в 4 раза больше времени, чем условный оператор C #, но все еще очень быстра и вряд ли станет основным узким местом в большинстве случаев использования.
Я приветствую любые мысли / замечания / критические замечания и т. Д. По поводу вышеизложенного (или о возможностях более богатой поддержки языка C # ... надеемся здесь ;-p).
источник
switch-case
заявления. Не поймите меня неправильно, я думаю, что это имеет место, и я, вероятно, буду искать способ реализации.Ответы:
Я знаю, что это старая тема, но в C # 7 вы можете сделать:
источник
После попыток сделать такие «функциональные» вещи в C # (и даже попытаться написать книгу об этом), я пришел к выводу, что нет, за некоторыми исключениями, такие вещи не слишком помогают.
Основная причина заключается в том, что такие языки, как F #, получают значительную силу от истинной поддержки этих функций. Не «ты можешь сделать это», но «это просто, это ясно, это ожидаемо».
Например, при сопоставлении с образцом вы получаете компилятор, сообщающий, есть ли неполное совпадение или когда другое совпадение никогда не будет найдено. Это менее полезно для открытых типов, но при сопоставлении различаемого объединения или кортежей это очень изящно. В F # вы ожидаете, что люди будут соответствовать шаблону, и это мгновенно имеет смысл.
«Проблема» заключается в том, что, как только вы начнете использовать некоторые функциональные концепции, естественно захотеть продолжить. Однако использование кортежей, функций, частичного применения методов и каррирования, сопоставления с образцом, вложенных функций, обобщений, поддержки монад и т. Д. В C # становится очень уродливым и очень быстрым. Это забавно, и некоторые очень умные люди сделали некоторые очень крутые вещи в C #, но на самом деле их использование кажется тяжелым.
То, что я часто использовал (через проекты) в C #:
** Но учтите: отсутствие автоматического обобщения и вывода типов действительно препятствует использованию даже этих функций. **
Все это говорит, как кто-то еще упомянул, в небольшой команде для определенной цели, да, возможно, они могут помочь, если вы застряли с C #. Но по моему опыту, они обычно чувствовали себя более хлопотно, чем стоили - YMMV.
Некоторые другие ссылки:
источник
Возможно, причина того, что C # не облегчает переключение типов, состоит в том, что это в первую очередь объектно-ориентированный язык, и «правильный» способ сделать это в объектно-ориентированных терминах - это определить метод GetRentPrice для Vehicle и переопределить его в производных классах.
Тем не менее, я потратил немного времени, играя с мультипарадигмыми и функциональными языками, такими как F # и Haskell, которые имеют такую возможность, и я встречал несколько мест, где это было бы полезно раньше (например, когда вы я не пишу типы, которые вам нужно включить, чтобы вы не могли реализовать на них виртуальный метод), и я бы приветствовал этот язык вместе с различающимися союзами.
[Редактировать: Удалена часть о производительности, поскольку Марк указал, что это может быть закорочено]
Другая потенциальная проблема связана с удобством использования - из последнего вызова ясно, что происходит, если совпадение не удовлетворяет каким-либо условиям, но каково поведение, если оно соответствует двум или более условиям? Должен ли он бросить исключение? Должен ли он вернуть первый или последний матч?
Для решения этой проблемы я обычно использую поле словаря с типом в качестве ключа и лямбда-значением в качестве значения, которое довольно лаконично построить с использованием синтаксиса инициализатора объекта; однако это учитывает только конкретный тип и не допускает дополнительных предикатов, поэтому может не подходить для более сложных случаев. [Примечание: если вы посмотрите на выходные данные компилятора C #, он часто преобразует операторы switch в таблицы переходов на основе словаря, поэтому, по-видимому, нет веской причины, по которой он не может поддерживать переключение типов]
источник
Я не думаю, что такого рода библиотеки (которые действуют как языковые расширения), вероятно, получат широкое признание, но с ними интересно играть, и они могут быть действительно полезны для небольших команд, работающих в определенных областях, где это полезно. Например, если вы пишете тонны «бизнес-правил / логики», которые выполняют тесты произвольного типа, как этот, и тому подобное, я могу видеть, как это будет удобно.
Я понятия не имею, может ли это когда-либо быть особенностью языка C # (кажется сомнительным, но кто может видеть будущее?).
Для справки, соответствующий F # примерно:
при условии, что вы определили иерархию классов в соответствии с
источник
Чтобы ответить на ваш вопрос, да, я думаю, что синтаксические конструкции сопоставления с образцом полезны. Я хотел бы видеть синтаксическую поддержку в C # для этого.
Вот моя реализация класса, который обеспечивает (почти) тот же синтаксис, который вы описываете
Вот некоторый тестовый код:
источник
Сопоставление с образцом (как описано здесь ), его целью является деконструкция значений в соответствии со спецификацией их типа. Тем не менее, концепция класса (или типа) в C # не согласна с вами.
С многопарадигмальным языковым дизайном нет ничего плохого, напротив, очень приятно иметь лямбды в C #, и Haskell может делать императивные вещи, например, IO. Но это не очень элегантное решение, не в моде на Haskell.
Но поскольку последовательные процедурные языки программирования могут быть поняты с точки зрения лямбда-исчисления, а C # хорошо вписывается в параметры последовательного процедурного языка, это хорошо подходит. Но если взять что-то из чисто функционального контекста, скажем, Haskell, и затем поместить эту функцию в язык, который не является чистым, то, делая это просто, не гарантирует лучшего результата.
Суть в том, что галочка соответствия шаблонам связана с языковым дизайном и моделью данных. При этом я не считаю, что сопоставление с образцом является полезной функцией C #, поскольку оно не решает типичные проблемы C # и не вписывается в парадигму императивного программирования.
источник
ИМХО ОО способ делать такие вещи - это паттерн Visitor. Ваши методы-члены посетителя просто действуют как case-конструкции, и вы позволяете самому языку обрабатывать соответствующую диспетчеризацию, не просматривая типы.
источник
Хотя переключение типа не очень C-sharpey, я знаю, что конструкция будет очень полезна в общем случае - у меня есть хотя бы один личный проект, который мог бы использовать его (хотя это управляемый банкомат). Много ли проблем с производительностью компиляции, с переписыванием дерева выражений?
источник
Я думаю, что это выглядит действительно интересно (+1), но с одной вещью нужно быть осторожным: компилятор C # довольно хорош в оптимизации операторов switch. Не только для короткого замыкания - вы получаете совершенно другой IL в зависимости от того, сколько у вас случаев и так далее.
Ваш конкретный пример делает то, что я считаю очень полезным - нет синтаксиса, эквивалентного регистру по типу, поскольку (например)
typeof(Motorcycle)
не является константой.Это становится более интересным в динамическом приложении - ваша логика может быть легко управляемой данными, обеспечивая выполнение в стиле 'движка правил'.
источник
Вы можете достичь того, что вы хотите, с помощью библиотеки, которую я написал, под названием OneOf
Основное преимущество перед
switch
(иif
иexceptions as control flow
) заключается в том, что он безопасен во время компиляции - нет обработчика по умолчанию или сбойОн на Nuget и нацелен на net451 и netstandard1.6
источник