Pull to refresh

Comments 22

Странно, из личного опыта могу сказать, что:

  1. Удобно использовать dynamic на уровне маппинга в ORMах типа Dapper'а. Кроме этого при десериализации можно порой обойтись dynamic'ом если надо быстро что то накостылять, а дтошки нет.

  2. Query expressions очень зашли в синтаксисе DSL фреймворка Sprache.

Хорошее замечание.

Относительно Sprache, query expression как раз удобен из-за необходимости "выпрямления" запроса

Parser<string> identifier =
    from leading in Parse.WhiteSpace.Many()
    from first in Parse.Letter.Once().Text()
    from rest in Parse.LetterOrDigit.Many().Text()
    from trailing in Parse.WhiteSpace.Many()
    select first + rest;

var id = identifier.Parse(" abc123  ");

Assert.AreEqual("abc123", id);
UFO just landed and posted this here

преимущество на совсем уж маленьких и одноразовых проектах.

Иногда API сервиса может диктовать внешний сервис (в бизнесе всё возможно). В одном проекте для входных данных эндпоинта мне пришлось использовать свойства `dynamic From ... dynamic To ... dynamic Cc ...` отправитель(и) и получатель(и) почт, а потом полученные данные я маппил в массив строк. В c# пока нет discriminated unions, чтоб можно было сделать проще `string | string[]`. Вот так и иногда приходится использовать `dynamic`.

Ну а как же наследование?

UFO just landed and posted this here
Это, типа, «Даже я, адвокат, требую для подсудимых смертной казни»? В смысле, как-то такие примеры не очень вдохновляют на использование.

А по существу:
1. Никогда не любил смешивать SQL-like syntax и ЯП. Данный же конкретный пример наводит на мысль, что что-то неправильно спроектировано. И очень хорошо, что это заметно на уровне языка, гораздо хуже, когда язык это скрывает.

2.
так как комбинация статической и динамической типизации могла быть невероятно мощным инструментом

ИМХО, очень вряд ли это могло бы случиться в C#. Мне больше всего нравится, как комбинация статики/динамики сделана, например, в TIS. С одной стороны — типов нет в принципе (при описании), мысль разработчика ничто не сдерживает, с другой — шаг влево/вправо,
5 + '3'
— сразу летят exception'ы, которые отлично заменяют компиляцию в плане безопасности. Это, конечно, много где сделано, можно хоть тот же OLE_VARIANT вспомнить, но без глубочайшей поддержки на уровне языка / стандартной библиотеки эффективности не добиться, а генетика у Шарпа для этой цели не та.
С одной стороны — типов нет в принципе (при описании), мысль разработчика ничто не сдерживает, с другой — шаг влево/вправо, 5 + '3' — сразу летят exception'ы

Это называется "строгая динамическая типизация", а не "комбинация статики/динамики"

Из моей практики, на одном проекте активно использовались dynamic типы для работы с JSON -ами, приходящими из базы. С одной стороны, не надо делать массу DTO- шек, и если структура документа в базе со временем меняется, то вроде бы как код продолжает работать без необходимости поддерживать сущности v1, v2, vN+1... Но преимущества мнимые, т. к. все начинает падать в рантайме, принося в .Net все "прелести" языков без строгой типизации. Бррр, до сих передергивает, как вспоминаю.

Другой случай, где я применял dynamic, на этот раз самостоятельно, заключался в том, что внешняя библиотека (OpenXML, но это не важно) предоставляла несколько почти идентичных классов, с одинаковым набором полей, но в разных пространствах имён. Никаких общих интерфейсов или предков они не имели. Расширить внешнюю библиотеку тоже нельзя. И нужно было написать конвертацию из обоих типов в наш внутренний, по возможности, избежав копипасты. Для этого у меня было два internal метода, принимающих аргументы из разных пространств имён, и перенаправляющих вызовы в private метод, с dynamic аргументом. Чтобы это все не отвалилось при апгрейде OpenXML, код был плотненько покрыт тестами. И долгое время для меня это был единственный более-менее оправданного применения dynamic на практике, но и то потом оказалось, что это вызывало проблемы у пользователей, работающих в sandbox environment-ах (подробностей не помню, если кому будет интересно, найду соответствующий тикет на гитхабе). Так что и от такого применения пришлось отказаться.

А в защиту query syntax скажу, что тоже не понимал, для чего им вообще пользоваться, пока не столкнулся с запросом, в котором цепочка джойнов добавляла по одному новому полю к анонимному объекту. С query- синтаксисом такой запрос оказался в несколько раз короче, и читается легче.

Немного из другой серии, но тоже из разряда бесполезного - это возможность иметь internal abstract член в публичном классе. Если унаследоваться из внешней сборки, переопределять этот член нельзя, т.к. он internal, но не переопределять тоже нельзя, т. к. он абстрактный. Такой вот курьез. Вроде и не баг, но и практической пользы ноль. Будет интересно, если кто-то придумает юзкейс, в котором это применимо.

Ещё одна вещь, которую вряд ли придумали бы в такой форме, если придумывали сразу, с нуля. "protected internal" является не взаимоусиливающей комбинацией "protected" и "internal", а действует как "или": чтобы получить доступ к члену надо быть наследником ИЛИ (не И) находиться в той же сборке (ср. с private static, например - на него одновременно распространяются ограничения private и static, усиливая друг друга). Зато логическое И выражается в виде модификатора "private protected", хотя от private в нём вообще ничего. Мне кажется, если бы не обратная совместимость, то "protected internal" должен был бы стать тем, что сейчас называется "private protected", а нынешний protected internal (с логическим ИЛИ) должен быть упразднен: если уж член protected, то ему обычно незачем быть публичным внутри своей сборки.

Немного из другой серии, но тоже из разряда бесполезного — это возможность иметь internal abstract член в публичном классе. Если унаследоваться из внешней сборки, переопределять этот член нельзя, т.к. он internal, но не переопределять тоже нельзя, т. к. он абстрактный. Такой вот курьез. Вроде и не баг, но и практической пользы ноль. Будет интересно, если кто-то придумает юзкейс, в котором это применимо.

Практическая польза тут — в возможности создания закрытых иерархий классов, которые невозможно расширить в другой сборке. Пример из Windows Forms: Image — Bitmap, Metafile. Эти классы являются обёртками над объектами GDI, и новых типов объектов в этой иерархии не предусмотрено.


Фактически, таким образом можно делать ООПшные аналоги Union-типов из ФП.

Странный аргумент в пользу query синтаксиса. Method chain так же можно сделать без вложенности:

 public IEnumerable<User> GetFriendsOfBlaBlaBla(User user, string name) =>
     user.Friends
       .Where(x => x.Name == name)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends)
       .SelectMany(x => x.Friends);

dynamic хорош для двойной диспетчеризации. В частности, паттерн Visitor с его помощью выражается намного проще.

Query expression с применением let сильно упрощает сложные запросы. В синтаксисе методов придётся протаскивать сложный анонимный тип.

Например, в библиотеке lin2d используется такая запись. Переписать на синтаксис методов, конечно, можно, но выглядеть будет жутко.

Вот жеж ахинея. Чуть позже разверну, если будет в этом смысл, после прочтения каментов...

Делегат это объявление сигнатуры функции. Его для этого и используют. И экспортируют. А не внутри функций. Как описать event лямбдами?

Про query и expression tree и сахар

Попробуйте написать аналог

From var1 in set

let var2=func(var1)

let var3=func2(var1)

From var4 in set2 join on...

Where var4 between var2 and var3

Select (f2=var2, f4=var4)

Ну смысл думаю понятен. Нельзя подобные конструкции яснее написать...В строчку, с трансляцией переменных.

Ну а в целом примеры какието не жизненные. Придуманные из пальца для статьи.

Как изначально джавист, могу сказать, что как реализованы евенты в C# — это то ещё зло. Мало того что «застолбили» такое удобное название переменной в ключевые слова (туда же идёт и params), так ещё и постоянно приходится думать обо всяких утечках и «застрявших событиях». И ещё не очень совместимо с «новой идеологией», как вы и написали.
Я-то ладно, уже давно на с# пишу — уже знаю хорошие практики, но джуны у меня регулярно косячат на event'ах. Приходится буквально семинары им устраивать с доской и партами.

Попробуйте написать аналог ...

Ну есть всякие SelectMany. Да и вообще иногда интересно декомпильнуть и посмотреть что именно скрывается за сахаром (Всякие Where() и лямбды — тоже сахар, кстати).
А что не так с событиями на шарпе?
Это целую статью писать надо. Но если совсем-совсем коротко: не стоит вводить в «ядро» языка то, чему место в стандартной библиотеке.

Я думаю, как минимум имеется ввиду, что если не отпишешься, то сборщик мусора не может подчистить объект. И потихоньку отъедается память.

Вот простой пример, обходите в глубину сколько влезет. Без убогих конструкций.

private static void SelectAll(IEnumerable<Friend> friends, List<Friend> all)
{
  if (friends?.Any() == true)
  {
    all.AddRange(friends);
    var sub = friends
      .Where(x=>x.Friends != null)
      .SelectMany(x => x.Friends);
    SelectAll(sub, all);
  }
}

Дальше оптимизируйте под свои нужды. Избавляйтесь от рекурсии, от параметра all и тд

Sign up to leave a comment.

Articles