Замечательное видео) Внесу свою лепту (как человек что много лет пишет на чистом С/Ассемблере, а сейчас пишу этот комеет как С# фулстек) 1) модификаторы доступа - это годнота. Если не нужно, то не используешь "лишнее". У С\C++ вместо internal вообще static (это при том, что там есть external) Я выступаю за то, чтобы был максимально гибкий контроль за областью видимости, потому как много раз видел, как сужение области видимости просто "схлопывает" тонны кода на выхлопе компилятора. И таких инструментов, которые несут смысл как для программиста, так и для компилятора очень мало, если так подумать 2) Мне очень нравится, что вложенные типы видят приватные поля тех, что выше по иерархии. Благодаря этому можно очень красиво делать реализации Строителей. Если делать как по науке, то нужно подводить желаемый тип под интерфейс, в котором можно менять значения полей. И сам интерфейс хорошо бы спрятать, чтобы уши не торчали... а так, вложил один тип в другой и всё готово. Потому как руки всё равно потянтся проверить, а не будет ли у типа фабричного метода, если конструктор приватный Про путаницу с модификаторами доступа по умолчанию -- полностью солидарен 3) Дженерики - это прям беда. Столько раз себя ловил на мысли, что это какой-то кастрат плюсовых шаблонов, что аж неловко становилось Они ну слишком базовые. Есть попытки в метапрограммирование (те же where и всякие контр/ко/ин-вариантности), но пользоваться ими очень неудобно, когда нужно работать с иерархией этих типов Спасаемся базовыми типами, как и было упомянуто в видео) Но лично моя боль №1 -- поведение структуры с интерфейсами Это прямо дичь. Моментально рушится вся идея строго значимого типа. Плюс к тому очень много странных ограничений (нельзя давать полям значения по умолчанию, вне конструктора без аргументов, но как только ты этот самый конструктор определяешь, то ты рушишь целую вереницу оптимизаций, что заботливо для тебя выстроили авторы языка) А те же readonly ref struct я смог применть только в паттерне итератор. И работает это только из-за костылей с утиной типизацией... вне этого как только у тебя появляется хоть маломальски выстроенная иерархия типов и ты хочешь внести "перформанса" , то ты быстро обнаруживаешь, насколько же у тебя связаны руки. Либо рефлексия, либо переписывать всё в совсем другой парадигме Я бы добавил интерфейсу гарантию, что он представляет только значимый тип (с синтаксисом для такого поведения). По сути легальный и чистый способ для утиной типизации С# получился очень удобным прикладным языком И он может решать целый спектр задач Потому его сейчас как лебедь/рак/щука тянут в сторону обратной совместимости, рядом новые и модные практики с функциональщиной, а особняком системные программисты, которым нужон High Performance Ещё раз выражаю благодарность за видео и труд! Смотрю тебя с большим удовольствием)
О дааа! Помню на первой работе иногда нужно было лазить в сорсы ANTLR, а он на старехонькой Java. И я прямо страдал без var и LINQ... Но это и была совсем старая Java..
В Java самая попоболь это отсутствие единого стандарта. Всякие там спринги, груви и прочий заопарк. 90% процентов Java чатика на проекте забито вопросами типо "вот я обновил версию библиотеки с х на y и вот тут у меня всё сломалось" или "взял последнюю версиюю исходников из репозитория и уменя всё сломалось". И вот когда видишь советы типа "надо взять вот эту весию либы X и вот эту версию либы Y. а вот этот код откатить/закоментить/переписать" волосы на голове шевелятся.
@@ruslooob лямбды, не умеющие менять значения переменных, недогенерики, отсутствие await, операторы перегружать нельзя, поэтому упаси вас бог создавать через new и сравнивать стринги через ==, прекрасное совершенно поведение типа long с недооптимизацией, странные моменты с уборкой мусора, и т.д., всего не упомнишь. Больше всего конечно трогают лямбды, await и недогенерики, 2023 год на дворе, остальное реже.
Ух, про switch прямо в точку. Помню сходил на неделю в отпуск и напрочь забыл его синтаксис. Дело было года два назад, когда он только входил в обиход. По части модификаторов доступа - там еще file завезли) а где internal, там и InternalsVisibleTo, так сказать) P.S. На фразе «С деструктором все еще хуже. Во-первых, он у нас… есть» заржал и подавился своим смузи
"главная проблема №1" - пустой каприз и я бы даже сказал дилетантство. Если ты так смело хочешь залезать в базовый класс и чтобы потомки ничего не заметили, ты явно ошибся методологией - тут один пусть, в Бейсик. 😆 Базовый класс ОБЯЗАН ломать потомков, ибо так и задумано: если ты что-то изменил в предке, значит потомки уже не могут рассчитывать на прежнее поведение и должны сами измениться. Просто представь современный автопром: базовая конструкция моделей ОДНА, а сверху нахлобучивается купе, седан и т.п. И тут бравый анжанер удлинняет колёсную базу. Как думаешь, модели не заметят этих изменений? :)
Не согласен) У нас есть очень много способов изменить базовый класс так, что бы не сломать наследников. Проблема из жизни: есть абстрактный репозиторий с большим количеством методов. Есть около 40 наследников. Все создаётся лишь через DI. Что бы реализовать новый метод в базовом репозитории мне нужна ещё одна зависимость: что-нибудь инфраструктурное. Она уже лежит в DI. Мне нужно добавить ее в конструктор и все готово было бы... ...но нет, нужно ее ещё добавить в 40 наследников 😭
1. В С++ если констуктор наследника будет по аргументам повторять базовый + без тела (только вызов конструктора базового класса), то можно написать внутри класса наследника class Derived : public BaseClass { using BaseClass::BaseClass; }
с парой проблем сталкивался,решения такие же были,как и ты предложил. но давайте посмотрим правде в глаза - многие из новых фич языка (вообще) крайне сомнительны и спорны( те же интерфейсы с реализацией по умолчанию)
У меня как-то было веселье с перегрузкой методов. Уже не помню деталей, но идея была в том что было 2 версии метода и одна принимала энум, а вторая инт. В итоге я передаю энум, а вызывается версия с интом.
я десь рік тому стикнувся з 10-ю проблемою, тоді теж мало не здурів і майже постарів на 50 років поки дебажив, в кінці в мені щось перегоріло і я просто змирився що це так не працює, розбиратись в причинах вже не було ні часу ні моральних сил, просто переробив і забув. Ти зараз прям витяг на "світ божий" коварне зло приховане в пітьмі, яке паскудить життя багатьом. Вважай - ти рятуєш людям нерви і час для чогось більш прекрасного, так тримати👍
Добротний відос. Тепер по пунктах 10) Все правильно, так влаштовані інтерфейси - я не хочу знати внутрішню імплементацію (вона взагалі може бути в іншій інтернал френдлі ассемблі, або замокана в тестах), а працювати лише з публічним контрактом, тому x і має бути 10 9) Теж логічно, але неочевидно - лише на досвіді трекаєш таке. 8) void - зло, a async void - ще більше зло :) 7) event - в топку, застаріло, краще юзати Rx. Конкатенація делегатів може бути корисною в маппері, наприклад, коли накидуєш на маппінг одного проперті декілька делегатів 5) Type inference - це біль C# та аналізаторів синтаксису. F# в цьому плані дуже органічний 4) protected - це біч ООП, коли писали все на Rich Domain Model, де дані та поведінка інкапсулювалися в самому класі і для модифікації функціональності лишалося засабкласитися з оверрайдами (привіт Template Method pattern). Pubilc модифікатор в інерфейсах юзаю лише тоді, коли далі йде дефолт імплементація - це дозволяє візуально відрізняти вже імплементовані методи від тих, які треба імплементувати в нащадках 2) Конструктор ще не можна передати як посилання на метод, типу .Select(Myobj.ctor) і доводиться писати .Select(_ => new Myobj()) 1) Вітаю, ти віднайшов property injection :) В Blazor є відповідний атрибут [Inject], де можна врізати свій сервіс без зміни всіх нащадків
10. Згоден, але кейс муторний и хотів би щоб мова якось уберігала від таких пострілів собі у ногу. 8. Так там навіть не було про async void... Там скоріше про те, що навіть Task біда, тому що він не Task 7. Чи можна було б у мапері реалізувати так без конкатенацїї делегатів? Так. Тому все ще вважаю, що конкатенація делегатів дуже стрьомна штука, що була додана у мову помилково. 2. Доречі цікавий кейс. 1. Property injection став більш прийнятним як на мене після введення слова required. До цього: стрьомна штука.
По поводу 8. Про void это не конкретно шарпа беда, а вообще всех языков, генеалогическм восходящих к Си. Поэтому в си возвращают 0, что значит, что процедура выполнена успешно.
Использовали сложение делегатов в продакшене. Был парсер документов, который менял состояние в зависимости от положения в документе, и на разных состояниях нужно было обрабатывать токены одним или несколькими анализаторами. Соответственно был словарь состояние-делегат, где делагат не обязательно один анализатор, а может быть несколько с разной логикой. И не то чтобы это было бы сложно сделать без суммирующихся делегатов, но если они уже есть, то почему бы их не заюзать.
Кому как, у меня каждый раз болят дженерики из 3-го места, ну и с ними всё время паровозиком идут массивы из 1-го места. Это массив, список, словарь, как их понимать? И каждый раз либо изобретаешь с нуля, либо тащишь свою библиотеку с решением. Создаёшь в каждой библиотеке модуль Autofac, автоматчески регистрируешь вот это всё... а под капотом боль от "а класс реализует дженерик интерфейс", а "интерфейс наследует этот дженерик интерфейс"? А атрибут? А как из этого безобразия создать инстанцию?... 🤯
Проблему с 1 места мне доводилось решать похожим способом, но со временем я понял, что лучше будет сделать отдельный класс RepoMappedEntittiesBase, который собственно и добавляет новый функционал и будет требовать уже для новых наследников передачу нужных данных. Тем самым мы и не нарушаем Open-Closed принцип. Как по мне оба подхода имеют смысл, т.к. все зависит от контекста того, какие изменения вы планируете внести.
Из всех перечисленных моментов, считаю худшими только с 10 места. Довольно часто сталкивался с проблемой из 7 места, из-за которой приходилось писать кучу костылей, однако спасибо тебе за её решение, ведь действительно запомнить disposables при подписке упрощает решение этой проблемы.
Обычно с коллегами обсуждаем новые фичи... и часть просто не используем))) чтоб не бесили. Как в шутке - "если вам не нравятся гейпарады, прекратите в них участвовать"
На счёт конструкторов: можно сгенерировать пачку паршл классов в которых определяется конструктор. Либо иньектить зависимости в свойства. Ну а 50 тестов всё ровно придётся изменить.
Насчет свитча орнул) Пока мы - джсеры, страдаем от того что из нашего языка пытаются сделать шарп постоянной копипастой паттернов от туда, то шарписты страдают от того к ним завозят паттерны из джса))) З.Ы. Для тех кто не въехал, в джс свитч кейс как он выглядит в плюсах или хексе это антипаттерн, который заменяют объектом, каждый из свойств которого является кейсом, который стрелочной функцией возвращает результат для кондишена
По девятому пункту (IQueryable и IEnumerable): если такая проблема появляется на продакшене, то у вас большие проблемы с тестированием своей работы и язык тут не при чем. Подобные проблемы у вас будет при использовании любого языка
1ое место. Был такой случай на первой самой работе. Был самым зеленым джуном среди сеньоров. И потел каждый раз, когда в процессе багфикса приходилось трогать "то что работает" и было написано опытными программистами. Но в один прекрасный день попался баг, в качестве решения которого, было необходимо добавить в конструктор базового класса некий объект. Как сейчас помню, звоню наставнику и несколько раз уточняю, что "пук среньк надо вот в базу добавить вот это, но там наследование 5> потомков и типа мне надо каждому из них в ктор пробросить этот тип..???" "- Да"....
А первая проблема у меня чаще решается практически полным отсутствием наследования, если честно. Если там какая-то общая логика, то её можно вывести в какой-нибудь класс-хелпер. С наследованием в принципе очень легко отстрелить себе ногу не в одном месте, так в другом. Была весёлая история, когда переопределили то ли Equals то ли GetHashCode в базовом классе сущности и у нас то ли работа с базой, то ли какие-то вычисления взорвались от этого в совсем другом месте
" у меня чаще решается практически полным отсутствием наследования, если честно." сейчас тебе мамкины архитекторы и мамкины тимлиды накидают г...внеца на вентилятор в комментах ))) А если серьезно, то я согласен, лепить где попало наследование что бы казаться умным бред, его надо применять там где действительно есть общая логика которую копипастить не стоит в классах, но любителей что то поменять в базовом классе всегда можно ткнуть носом в SOLID, а точнее в Open/Closed, но на практике лучше конечно применять наследование там где это РЕАЛЬНО нужно, а не потому что "мы же умные программисты". Последнее время вообще наблюдаю тенденцию что сейчас код пишется не для решения бизнес задач, а для применения паттернов, архитектур, "умных идей из книжек" и т.д., то есть сейчас не принято думать при написании кода, главное написать код "поумнее" и главное чем сложнее тем лучше, что бы не выглядеть идиотом, потому что забыли про главный принцип программирования KISS, который я считаю поважнее будет чем SOLID и ООП вместе взятые. )))
Сложные многоуровневые разветвленные иерархии точно могут быть злом и доставлять огромное количество проблем. Иерархии в 1 уровень, вполне часто могут быть неплохим решением: базовый репозиторий и пару десятков наследников и подобные варианты. Вот на таких вариантах первая боль и отстреливает часто.
@@DevJungles да, согласен, 1 уровень, максимум 2 это норм. У нас в проекте TL является "архитектурным астронавтом" (загуглите термин кому интересно что это за тип программистов), так вот у нас иерархия в 20-30 бессмысленных классов, черт ногу сломит, а главное что необходимости в этом вообще нет, но у нас в конторе главное написать "код позамудренее" и чем больше он непонятный и сложный тем ты круче. Так что согласен с @Dev Jungles - Andrii Podkolzin на все сто процентов, 1 уровень вообще идеально, 2 допустимо но нужно точно иметь на это обоснование. 🙂
По поводу последней проблемы: в Rider есть удобная функциональность, добавляющая аргументы в функцию, если в одном из мест вызова дописать этот аргумент. Это же применяю и при добавлении нового параметра в базовый класс. Так же можно через меню рефакторинга это же сделать - в popup окне вписываешь название и типа аргумента, и он появляется во всех наследниках. Не знаю, как там в VS - давно не использовал
Диалог да, а когда фактически изменил конструктор (или любой другой метод), то прям из контекстного меню можно добавить новый параметр во все наследники (или реализации)
Кстати, я про IEnumerable/IQuerible всегда факаплю сам, но в приведенном примере еще большее зло - ToList(). Я даже долгое время думал, что для листа ToList вернет тот же самы инстанц, но в реализации увидел, что всегда создается новый экземпляр
@@DjonNIK777 уточню: когда прописываешь новый аргумент, он выделяется такой белой рамочкой, и если в этот момент нажать Альт+Энтер или какая у вас комбинация для квик фиксов, то там первым пунктом будет применение рефакторинга
@@DevJungles Для is вывод и не нужен. Но есть проблемы с тем, что дальше делать с этим знанием и с эффективностью реализации подобного оператора. А вообще универсальным решением были бы макры или пользовательские расширения языка. Тогда каждый мог бы написать библиотеку своих расширений и жить удовлетворённым.
А в целом - да, вроде косяков хватает, но с многими или вообще не сталкиваешься или они лишь чуточку портят жизнь. А потом садишься за Питон или того хуже Джаваскрипт и стреляешь себе в ногу на пустом месте. Например в Питоне свойства объекта в конструкторе надо писать через self., иначе это будут статические свойства. Я разок так выстрелил себе в ногу. Вон стильно-модно-молодёжный Rust из коробки не имеет yield, пользуйтесь библиотеками :( Подозреваю что если пописать на нём подольше, то тоже можно всякого найти
Немалую часть проблем фиксят линтеры и копайлот. Лично мне не нравится, что когда у меня есть интерфейс с синхронным методом, я подключаю асинхронную реализацию (хз, например перехожу на новую бд, к которой драйвер предоставляет возможность делать асинхронные вызовы), так вот теперь эти таски пронизывают код полностью. Придётся переписать все вфункции, чтобы они работали с тасками. И это прям вымораживает. Какого чёрта вообще моя бизнес логика должна знать синхронный или асинхронный вызов порта она должна вызывать (например в гексагональной архитектуре)? Бл не должна зависеть от инфра уровня. А тут язык подсовывает явную зависимость.
1. Мне кажется, эти проблемы копайлот не фиксит вообще никак. 2. Вполне реально выстроить слой бизнес логики без тасок и асинхронности. В том примере, что приводите судя по всему бизнес логика вызывает слой данных. Это ок в трехслойке, но не ок в гексагональной. Там бизнес модель в центре и не зависит ни от чего.
Насчет конструкторов вариантов не особо много, средствами языка сейчас, как вариант, только добавлять вторым конструктором, и вызывать из первого с созданием нужного класса. Но если нужно какую-то зависимость протаскивать, то тут без изменений никак не обойтись. Жаль райдер не умеет сигнатуры всех зависимых конструкторов обновлять. С языковой конструкцией не хватает какого-то наследования конструкторов, возможно. Так, чтобы если есть класс A с недефолтным конструкторов, то в классе B : A, он сразу был бы доступен, с возможностью переопределения через override. Так, если бы в наследниках не было бы специализированных конструкторов, тогда кода пришлось бы меньше лопатить. (Но если в наследниках есть дополнительные параметры, то всё равно фигачить придется) По-моему тут особо вариантов нет, если появляется необходимость какую-то чувствительную часть кода подергать, то к этому надо быть готовым. Вариант с моделькой с сервисами в приведенном примере считаю перебором. Если у базового класса много зависимостей, которые ещё и часто меняются, и не нужны наследникам, то это скорее всего признаки плохой архитектуры.
Спасибо за очередное полезное!) Никак не пойму как до такого уровня подняться. Ты не рассказывал про свой путь, что надо сделать? Не поступать в вуз, сразу стартапы, куча пет проектов и потом куча ком. проектов, не жениться (чтобы не отвлекали), с друзьями никуда не ходит и пр. Поделись лайф хаком, очень интересно!)
Ахахаха)) Да фиг его: ничего такого вроде не было. Мне кажется тут сыграло просто время движения: если 10 лет заниматься разработкой постоянно стараясь узнать что-то новое и сделать что-то новое, без экстрима в плане темпа, но и без длинных пропусков, то все сработает)
Самое забавное, что я даже знаю это название и даже видел его сорсы и полтора проекта на нем где-то в 18м году) Но уже тогда, увы, и язык и проекты были заброшены
На счет первого пункта. Если я не ошибаюсь , то можно вместо класса агрегатора и использовать атрибут , наподобие инжект в ангуляр, я недавно натыкался на статью. Суть в том что тебе вообще не надо писать зависимости в наследниках , а лишь указать аттрибут в базовом и он будет применять все di в наследниках
У меня проблема, которая на первом месте, была в ts'e, только там в конструкторе было 17 сервисов, в родитель я передавал 10, у того был еще один родитель с 4 параметрами. И к этим четырём параметрам надо было добавить пятый... Энивей у свитч экспрешена первое место у меня
К обходу 7-й проблемы в пример можно ещё библиотеку System.Reactive привести. Там тоже логика помещения подписки в IDisposable используется. Также есть механизмы адаптации классических событий. Но, с другой стороны, это уже не Subscriber/Puslisher, а Observable/Observer получается.
Если честно, несколько раз пытался вкатится в System.Reactive, но как-то все время задачи выглядили слишком простыми, не стоящими того, что бы разбираться в этой технологией... ..в общем сложновата она на вход в чем-то.
@@DevJungles Да, сложная вещь, объемная. Я с WPF в основном работаю, там неплохо весьма приживается эта технология. А вот WEB, например, сложно представить, как её использовать.
По поводу первого места. Мне не понятно, по какой причине нужно постоянно переопределять конструкторы. Этой проблемы могло бы не быть, если бы не пришлось этого делать. При этом они не обязательно что-то делают, они просто перебрасывают параметры и просто существуют. Возможно есть этому объяснение, которое я не знаю и это всё оправдано. Но например в php такого нету, конструктор можно сделать один раз в родителе и иметь кучу наследников, которые будут пользоваться уже существующим конструктором. В тайпскрипте тоже
Согласен. Кейс, когда конструктор не меняется можно было бы запросто решить. Более сложный кейс, когда наследники имеют больше зависимостей все равно бы остался, но уже стало бы легче.
Ну. Большая часть это не проблемы языка. Но с чем согласен, это с войдом. Войд - это реально боль, если тебе нужно библиотеку для команды делать. Приходится либо одинаковую логику мутить, либо вводить какое-то извращение в духе Unit, Empty, Nothing и всем говорить, что оно есть. И проблема со вторым подходом, в том что шарп начинает ворнинги сыпать, дескать ты результат не используешь, а ну как сделай Nothing _ = Lib.Execute();
Тема недоделаных дженериков раскрыта не до конца. Стандартные коллеккции не реализуют коваринтность, нет нормальной контрваринтности типа возвращаемого значения в реалилизциях методов базового класса. Нельзя сделать where T is SomeAbstractClass - нужно для SomeAbstractClass делать интерфейс ISomeAbstractClass. В джаве это реализовано гораздо лучше. Благо на бэкенде магия джеренриков нужна очень редко.
Не соглашусь... Давненько видел джаве, но тогда там дженерики меня прямо ужасали. Там не было generic constaints(может добавили?) и не было возможности сделать Generic от value-type: ArrayList, а только от Integer(который ref-type)
Сравниваю с котлин. 1. Проблема 10 - нету в таком виде, в перекрытых классах нельзя ставить дефолты 2. Проблема 9 - и есть и нет, да можно не то расширение вызвать, но нет идеи query able для внешних вызовов. Но проблема рантайма зато есть. Проблема есть и даже хуже но не из за языка, а jvm, jar с одинаковыми классами 3. Проблема 8 - нету, Unit (void) это тип 4. Проблема 7. - нету так как благо нет понятия событий 5. Проблема 6. - нету свитч (when) сразу был выражением, и не требуется break, как в golang, по умолчанию брек. 6. Проблема 5. - нету, вывод типов умеет из параметров выводить 7. Проблема 4. - другая, мне совсем не нравится, что в котлин по дефолту паблик, я бы предпочёл приват. Но дефолт тем не менее всюду один 8. Проблема 3. - другая, тоже сложности с апкаст можно сделать, зато рефлексия в куче контекстов не сработает ибо type erasure, генерики мне чуть больше в шарпе нравились, но правда хаки со звёздочкой там не работают 9. Проблема 2. Нету, явное ключевое слово constructor + init. Но в целом я предпочитаю приватные конструкторы и факторки в компаньонах 10. Проблема 1. Такая же. Специфика модели классов которая у джавы и Шарпа почти одна. Поэтому стараюсь меньше наследования, больше композиции, меньше конструкторов, больше фабричных методов
Потрясающий комментарий! Спасибо за подробный ответ: всего несколько раз смотрел Котлин. Но он доказывает, что мы не стоим на месте: были ошибки, мы(как индустрия разработки) их исправили.
Языки это ещё и рантайм под капотом. Например Kotlin основной и любимый собственно как язык уже 6 лет. Но JVM и джавовская экосистема которая у него под капотом такая мерзкая, что активно для новой разработки переходим на голанг, который как язык по примитивнее, зато всятэкосистема прямо очень хорошая. Не языком единым...
При просмотре подумал что прсто дженерики появились в netFramework 2. И не свящано с историей создания... создатели специально отказались от дженериков?
Половина проблем просто надуманы. А насчёт массивов и генериков и общего кода что мешает использовать интерфейсы? У массива спокойно есть генериковый интерфейс IReadOnlyList`T, например.
Да, есть способы выкрутится. Но, если работаешь в уровня рефлексии и пишешь какой-то обощенный код, то GetType().IsGenericType нужно помнить, что массивы туда не попадают.
30:00 а ещё я в райдере когда копирую файл с классом, вставляю, меняю ему имя, и оно не меняет имя класса внутри! и его приходится менять в двух местах!
Речь конечно про с# но добавлю по поводу косяков, в entity framework 7 завезли column json, только нельзя чтобы классы в этих колонках имели поле с названием id, будет вылетать ошибка при запросах , так как конфликтует как я понял с ихним десерелизатооом .
Вот женерики да, прям иногда хочется хитрых проверок навернуть, и так чтобы "оно само" потом работало при использовании. А в итоге получаются костыли с кучей наследований. А на счёт конструкторов - у меня была идея, что если бы на вход можно было передавать какой то класс (или анонимный тип) который может деконструктиться в нужный список аргументов прямо при передаче в конструктор. var arguments = new SomeClassArgsRecord(a, b, c); var someClass = new SomeClass(arguments.Deconstruct()); // new SomeClass(a, b, c)
На самом деле: мы все реже пишем оператор new, все чаще объекты создаются контейнром, потому именно передача параметров редко отстреливает прямо так серьезно.
Ну да, но это нужно постоянно помнить А ещё легко сделать классный метод для работы с IEnumrrable, а потом "случайно" его заюзать для какого-то Queryable
Хорошее видео, насчёт конструкторов с абстрактными классами полностью согласен, а так же с асинхронными и не асинхронными перегрузками. Как по мне уже давненько пора отказаться от явного применения Task с асинхронными методами и отдать это на вотчину компилятора, т.к. нам, как правило, нужен дженерик объект таска, а не сам таск,🤘
@@DevJungles ну, такое, я бы предпочел асинхронные методы суффиксом отмечать, и, возможно, чтобы явный тип был указан при наведении на метод. С var же так работает, почему бы не сделать что-то подобное. А возвращаемый тип, на самом деле не всегда говорит о том, что метод асинхронный, не знаю, доводилось ли ковыряться в либах к sql различным, там можно часто встретить методы, которые возвращают таск, но при этом прям параметром принимаю булевый флаг async и если ты выбрал false, то у тебя будет синхронный метод беспантово завернут в таск, такое..
Первое место жиза, но только не понятно как настроить его для DI. Есть видосы на эту тему? Еще тоже бесит что он автоматом не указывает дженерик, я думаю это могут добавить IDEшках как авто заполнение.
Смотря каким DI пользуетесь. В MicrosoftDI Add(Singleton/Scoped/Transient)(provider=>/*тут выбираете из провайдера что надо и конструируете экземпляр класса*/)... посмотрите повнимательнее методы регистрации типов в вашем DI должно быть что-то похожее
@@DevJungles positional record це такий: public record MyRecord(string Var1, int Var2); Він генерується у клас з конструктором, з додатковою логікою на порівняння типів. Звичайний record наступний: public record MyRecord { public string Var1 { get; init; } ... } Він генерується як звичайний клас, взагалі без нічого. Тож незрозуміло, який в ньому сенс. }
Зачем слайды на английском? %) И вы похоже упорный девелопер - спеллчекер пытался направить на путь истинный, подсвечивая "Finilizer", но не смог победить :D Ну и "mension" тоже
Тоже отмечу жуткую боль от дженериков. Они вроде бы неплохие, но не хватает кучи инструментов (по типу усилений ограничений по иерархии интерфейсов, например, и других). Тему самой главной проблемы (№1), честно говоря, не очень понял. Как изначальную проблему (наследование реализаций в программировании бьёт по рукам ещё со времён множественного наследования), так и решение-костыль (оно же усложняет чтение класса и параметров конструктора). Не совсем понимаю контекста, но разве использование Type Object паттерна не излечит от этой проблемы? Или декоратор, опять же.
@@DevJungles нет наследования - нет проблем протягивания зависимостей через всю иерархию. Что-то в этом роде)) Но это, наверное, я просто изначальную проблему не так понял.
его делали так по аналогии с F#, но там он добрый и ласковый друг, а здесь какой-то огузок, но выручал меня пару раз в связкой с ValueTupple может отлично заходить в некоторых ситуациях.
Занимался написанием компилятора для API одной системы... И пришла мысль что просто так проще компилятор реализовать, отличается порядок лексем. Отлично же, меньше возвратов назад делать, быстрее компиляция
@@DevJungles Наверное потому, что у меня был опыт работы на одном из проектов с таким объектом , который содержал в себе сотню полей, всегда частично заполненных, и ты никогда не знал, какие именно будут заполнены. Это был ад адский. Видимо, я получил психологическую травму, от которой так и не оправился))) Снизить поражающий эффект этого антипаттерна можно, если все поля будут обязательны, инициализироваться через конструктор, рид онли. Тогда, по крайней мере, ты будешь знать, глядя на этот, тасказать, ДТО, что от него ожидать. А что делать, если у тебя сложная иехархия базовых (абстрактных) классов (а у меня на текущем проекте именно так), делать еще один gob? и сколько их будет? В целом это системная проблема, которая говорит о том, что что-то сильно не так в королевстве Датском Для себя решил, что лучше терпеть эту боль, но следовать шаблону DI
Я может не слишком хорошо сформулировал идею... Идея для каждого класса с множеством аргументов в конструкторе делать отдельный класс аргументов. Если есть иерархия, то повторять ее в этих классах аргументах. Все отмечать required { get;init; } Здесь в самом конце видео еще раз к этому возвращаюсь как раз: ruclips.net/video/euZvNDFwTQ8/видео.html
по поводу конструкторов -- наследование зло)) даже если ты и заюзаешь предложенный воркараунд, это не решает главную (и по факту единственную) проблему -- ты всё равно поменял поведение 50ти наследников, только теперь ты об этом скромно умалчиваешь в своём PR. не надо так делать. если зависимость какая-то опциональная, то можно поменять абстрактный класс на интерфейс с дефолтными реализациями и туда докинуть приватный геттер с дефолтной реализацией возвращать какой-нибудь стаб или нулл, а дальше уже по контексту. энивэй, сама по себе проблема больная, но она больная не просто так.
В целом согласен, но... Но у нас уже есть очень много способов более серьезно изменить поведение наследников и не сообщить об этом в PR: изменение тела базового виртуального метода, например. А вот опциональные зависимости меня обычно тоже довольно сильно пугают... В этих случаях мне скорее хочется решить это: - в идеале композицией - хотя бы наследованием.
тоже с топ 1 не согласен, предлооженное решение еще и приведет к созданию GodObject с кучей всякой нужной и не очень херней внутри, и там зависимости и использование вообще хер найдешь. у меня в одном рефаторинге пришлось поменять по этой схеме много конструкторов, но только благодаря их явному изменению Я смог заметить другую прблему, что в многие классы добавлялись вообще не нужные им зависимости просто потому что этого требовал базовый класс. в итоге было найдено кривое наследование.
@@DevJungles может привести если начнут пихать в класс все подряд, типа "удобно же" и тогда есть шанс появления класа который будет в себе содержать резолв всех зависимостей и его будут пихать везде. тогда может быть уже сложно понять какие из зависимостей где используются. по сути получится внедрение репозитория зависимостей
Я нашёл для себя, что 3 "самых худших момента", мне были уже знакомы и я так или иначе с ними сталкивался 🤯 Ну и остальные по мелочи знакомы были, хы. Я шо профи?
@@DevJungles В данном примере переполнения памяти не будет. Данные не будут все ЗАкачаны в память, а будут ПРОкачены через локальный компьютер, но при этом фильтр хоть и локально, но работать будет. А вот если бы в примере был OrderBy или GroupBy, то все описанные страшилки случились бы.
@@DevJungles да сих пор без подсказок среды не напишу. Но не согласен и с нападками, и представленными альтернативами. Дело в том что, switch как выражение, может, много больше по сопоставлению кейсов чем стандартный switch, и поэтому варианты из других языков не кажутся вдохновляющими. И естественно он должен вернуть что-то , когда ничего не совпало. Почему там _ , а не default -- загадка.
25:48 думаю кто можно было отойти, создав ещё один пустой не дженерик интерфейс, и наследовать его от дженерикового, и уже в is проверять на не дженериковий интерфейс
Если ограничение не время, а пропускная способность головы, то это может быть небольшой проблемой) Теперь вопрос: так ли мы уверены, что нас ограничивает время? Что бывает чаще: в конце рабочего дня вы хорошо поработали, есть ещё много дел, вы бороды и веселы, но уже 19:00 и пора домой. Или 17:00 и лузится стекающая изо рта от тупняка начала капать на стол))
Еще после котлина раздражает то что все по-умолчанию мутабельно. И I в названиях интерфейсов, которое по началу кажется здравой идеей на деле полная ерунда, ну больше выбешивает конечно IReadOnly, это просто супербойлерплеейт, ведь для хорошего тона все должно быть ридонли, в итоге у тебя весь код с полями засран этими IReadOnly... . Как по-мне это самая серьезная ошибка, которая говнит весь clr, и никакой новый язык (аля котлин для clr) это уже не исправит в отличии от других ошибок из видео Ну еще отсутсвие var в полях, который тоже очень сильно бойлерплейтит в некоторый случаях. А еще async/await после котлина это просто ужас, просто не хочется возвращаться в c# когда вспоминаешь о них и о cancellationToken, крайне недальновидное решение которое язык подпортило, конечно их можно оживить и адаптировать если сделать новый clr язык правда с костылями.
По мутабельности соглашусь, даже хотел поставить в видео, но потом решил не делать: много про это сказано и, пожалуй, это дань тому как тогда видели парадигму. А можешь подробнее сказать про cancellationToken и async/await? Что с ними не так?
@@DevJungles 1) Синтастический бойлерплейт постоянно надо писать Task и await хотя на деле для компилятора в этом нет необходимости все и так понятно. 2) Сложно провести аналог, ведь корутины это немного другое, но вообщем в котлине с канселяцией все проще ибо этот токен уже вшит в корутину и есть дочернии опреации которые канселятся вместе с родителем и следовательно не нужно постоянно прокидывать и комбинировать эти кансел токены и следить за этим.
Из относительно новинок ещё можно вспомнить проверку на null через is (pattern matching), а не == Способ через is рекомендуется самим майкрософтом, чтоб случайно не попасть на переопределение оператора == И всё было бы хорошо, если б этот is работал везде. На текущий момент его нельзя использовать в expression-ах (в отличие от лямбд, что дико путает). Т.е. во всех linq-выражениях на is будет ругаться... По итогу в коде получается сборная солянка, а ты постоянно натыкаешься на ошибку от IDE в linq... 🤬
"чтоб случайно не попасть на переопределение оператора ==" - какой идиот придумал обходить переопределение операторов?!!? Их СПЕЦИАЛЬНО ПРИДУМАЛИ, чтобы глупый канпелятор знал, как ПРАВИЛЬНО сравнивать объекты.
"Современные" языки не проще, они еще просто не столкнулись с теми проблемами, с которыми уже десяток лет сталкиваются разрабы. Ваше утверждение как слова Бендера из футурамы про казино.
@@DevJungles да, туплю - это интерфейсы, их реализовывать придется. Хотя, можно, протыкать - может, там - просто... Найти реализацию IMapper и от нее унаследоваться... Хз, мысль, просто пришла...
Чел, хорош , контент вообще в кайф!! Можно вот этого вот всего почаще❤❤❤
с языка)) всем маслятам маслят )))
Вы перепутали канал)
Замечательное видео)
Внесу свою лепту (как человек что много лет пишет на чистом С/Ассемблере, а сейчас пишу этот комеет как С# фулстек)
1) модификаторы доступа - это годнота. Если не нужно, то не используешь "лишнее". У С\C++ вместо internal вообще static (это при том, что там есть external)
Я выступаю за то, чтобы был максимально гибкий контроль за областью видимости, потому как много раз видел, как сужение области видимости просто "схлопывает" тонны кода на выхлопе компилятора. И таких инструментов, которые несут смысл как для программиста, так и для компилятора очень мало, если так подумать
2) Мне очень нравится, что вложенные типы видят приватные поля тех, что выше по иерархии. Благодаря этому можно очень красиво делать реализации Строителей.
Если делать как по науке, то нужно подводить желаемый тип под интерфейс, в котором можно менять значения полей. И сам интерфейс хорошо бы спрятать, чтобы уши не торчали... а так, вложил один тип в другой и всё готово. Потому как руки всё равно потянтся проверить, а не будет ли у типа фабричного метода, если конструктор приватный
Про путаницу с модификаторами доступа по умолчанию -- полностью солидарен
3) Дженерики - это прям беда. Столько раз себя ловил на мысли, что это какой-то кастрат плюсовых шаблонов, что аж неловко становилось
Они ну слишком базовые. Есть попытки в метапрограммирование (те же where и всякие контр/ко/ин-вариантности), но пользоваться ими очень неудобно, когда нужно работать с иерархией этих типов
Спасаемся базовыми типами, как и было упомянуто в видео)
Но лично моя боль №1 -- поведение структуры с интерфейсами
Это прямо дичь. Моментально рушится вся идея строго значимого типа. Плюс к тому очень много странных ограничений (нельзя давать полям значения по умолчанию, вне конструктора без аргументов, но как только ты этот самый конструктор определяешь, то ты рушишь целую вереницу оптимизаций, что заботливо для тебя выстроили авторы языка)
А те же readonly ref struct я смог применть только в паттерне итератор. И работает это только из-за костылей с утиной типизацией... вне этого как только у тебя появляется хоть маломальски выстроенная иерархия типов и ты хочешь внести "перформанса" , то ты быстро обнаруживаешь, насколько же у тебя связаны руки. Либо рефлексия, либо переписывать всё в совсем другой парадигме
Я бы добавил интерфейсу гарантию, что он представляет только значимый тип (с синтаксисом для такого поведения).
По сути легальный и чистый способ для утиной типизации
С# получился очень удобным прикладным языком
И он может решать целый спектр задач
Потому его сейчас как лебедь/рак/щука тянут в сторону обратной совместимости, рядом новые и модные практики с функциональщиной, а особняком системные программисты, которым нужон High Performance
Ещё раз выражаю благодарность за видео и труд!
Смотрю тебя с большим удовольствием)
1.2. согласен)
3. Я вот и рад, что не стали брать шаблоны из плюсов целиком: уж очень они сложны, но то, что затянули явно недостаточно, это тоже факт
Хаха, я смог заполнить switch expression. Буквально всего то пара лет прошло) Теперь осталось запомнить синтаксис его паттерн матчингов.
О дааааааа....
На канале, кстати, есть ролик про pattern matching... Я там тоже немного горю от синтаксиса)
Мне пришлось перейти с C# на Java, ребят. Как же вы счастливы, ребят, вы не понимаете, насколько C# кажется прекрасным после перехода на джаву!
О дааа! Помню на первой работе иногда нужно было лазить в сорсы ANTLR, а он на старехонькой Java. И я прямо страдал без var и LINQ... Но это и была совсем старая Java..
В Java самая попоболь это отсутствие единого стандарта. Всякие там спринги, груви и прочий заопарк. 90% процентов Java чатика на проекте забито вопросами типо "вот я обновил версию библиотеки с х на y и вот тут у меня всё сломалось" или "взял последнюю версиюю исходников из репозитория и уменя всё сломалось". И вот когда видишь советы типа "надо взять вот эту весию либы X и вот эту версию либы Y. а вот этот код откатить/закоментить/переписать" волосы на голове шевелятся.
зачем вообще жаба, если есть котлин
Жаба - лютое говнищще из прошлого века, зачем на него переходить?? Котлина мало что ли?
@@ruslooob лямбды, не умеющие менять значения переменных, недогенерики, отсутствие await, операторы перегружать нельзя, поэтому упаси вас бог создавать через new и сравнивать стринги через ==, прекрасное совершенно поведение типа long с недооптимизацией, странные моменты с уборкой мусора, и т.д., всего не упомнишь. Больше всего конечно трогают лямбды, await и недогенерики, 2023 год на дворе, остальное реже.
Ух, про switch прямо в точку. Помню сходил на неделю в отпуск и напрочь забыл его синтаксис. Дело было года два назад, когда он только входил в обиход.
По части модификаторов доступа - там еще file завезли) а где internal, там и InternalsVisibleTo, так сказать)
P.S. На фразе «С деструктором все еще хуже. Во-первых, он у нас… есть» заржал и подавился своим смузи
😁😅😅😅
Читаешь такой книжочку/статейку по сишарп, встречаешь слово "деструктор", реакция: "А, че? точно.."))
"главная проблема №1" - пустой каприз и я бы даже сказал дилетантство. Если ты так смело хочешь залезать в базовый класс и чтобы потомки ничего не заметили, ты явно ошибся методологией - тут один пусть, в Бейсик. 😆 Базовый класс ОБЯЗАН ломать потомков, ибо так и задумано: если ты что-то изменил в предке, значит потомки уже не могут рассчитывать на прежнее поведение и должны сами измениться.
Просто представь современный автопром: базовая конструкция моделей ОДНА, а сверху нахлобучивается купе, седан и т.п. И тут бравый анжанер удлинняет колёсную базу. Как думаешь, модели не заметят этих изменений? :)
Не согласен)
У нас есть очень много способов изменить базовый класс так, что бы не сломать наследников.
Проблема из жизни: есть абстрактный репозиторий с большим количеством методов. Есть около 40 наследников.
Все создаётся лишь через DI.
Что бы реализовать новый метод в базовом репозитории мне нужна ещё одна зависимость: что-нибудь инфраструктурное. Она уже лежит в DI. Мне нужно добавить ее в конструктор и все готово было бы... ...но нет, нужно ее ещё добавить в 40 наследников 😭
1.
В С++ если констуктор наследника будет по аргументам повторять базовый + без тела (только вызов конструктора базового класса), то можно написать внутри класса наследника
class Derived : public BaseClass {
using BaseClass::BaseClass;
}
Неплохая фича, но, наверное, не работает если добавить аргументы в наследнике...
@@DevJungles да, это уже не сработает, но в видео было так, что набор аргументов одинаковый)
Не знал о некоторых проблемах даже, повезло-повезло.
Спасибо, интересный формат!
Невероятно интересный видос. Большое тебе спасибо ❤
с парой проблем сталкивался,решения такие же были,как и ты предложил.
но давайте посмотрим правде в глаза - многие из новых фич языка (вообще) крайне сомнительны и спорны( те же интерфейсы с реализацией по умолчанию)
Очень интересно! Спасибо)
Крутое видео)
Крутое видео. Спасибо за воркэраунды для некоторых проблем!
У меня как-то было веселье с перегрузкой методов. Уже не помню деталей, но идея была в том что было 2 версии метода и одна принимала энум, а вторая инт. В итоге я передаю энум, а вызывается версия с интом.
очень хорошее видео, благодарю за труд
я десь рік тому стикнувся з 10-ю проблемою, тоді теж мало не здурів і майже постарів на 50 років поки дебажив, в кінці в мені щось перегоріло і я просто змирився що це так не працює, розбиратись в причинах вже не було ні часу ні моральних сил, просто переробив і забув.
Ти зараз прям витяг на "світ божий" коварне зло приховане в пітьмі, яке паскудить життя багатьом.
Вважай - ти рятуєш людям нерви і час для чогось більш прекрасного, так тримати👍
Крутий комент)
Радий, що відео сподобалося)
Добротний відос. Тепер по пунктах
10) Все правильно, так влаштовані інтерфейси - я не хочу знати внутрішню імплементацію (вона взагалі може бути в іншій інтернал френдлі ассемблі, або замокана в тестах), а працювати лише з публічним контрактом, тому x і має бути 10
9) Теж логічно, але неочевидно - лише на досвіді трекаєш таке.
8) void - зло, a async void - ще більше зло :)
7) event - в топку, застаріло, краще юзати Rx. Конкатенація делегатів може бути корисною в маппері, наприклад, коли накидуєш на маппінг одного проперті декілька делегатів
5) Type inference - це біль C# та аналізаторів синтаксису. F# в цьому плані дуже органічний
4) protected - це біч ООП, коли писали все на Rich Domain Model, де дані та поведінка інкапсулювалися в самому класі і для модифікації функціональності лишалося засабкласитися з оверрайдами (привіт Template Method pattern).
Pubilc модифікатор в інерфейсах юзаю лише тоді, коли далі йде дефолт імплементація - це дозволяє візуально відрізняти вже імплементовані методи від тих, які треба імплементувати в нащадках
2) Конструктор ще не можна передати як посилання на метод, типу .Select(Myobj.ctor) і доводиться писати .Select(_ => new Myobj())
1) Вітаю, ти віднайшов property injection :) В Blazor є відповідний атрибут [Inject], де можна врізати свій сервіс без зміни всіх нащадків
10. Згоден, але кейс муторний и хотів би щоб мова якось уберігала від таких пострілів собі у ногу.
8. Так там навіть не було про async void... Там скоріше про те, що навіть Task біда, тому що він не Task
7. Чи можна було б у мапері реалізувати так без конкатенацїї делегатів? Так. Тому все ще вважаю, що конкатенація делегатів дуже стрьомна штука, що була додана у мову помилково.
2. Доречі цікавий кейс.
1. Property injection став більш прийнятним як на мене після введення слова required. До цього: стрьомна штука.
Дяка за крутий і розгорнутий комент)
По поводу 8. Про void это не конкретно шарпа беда, а вообще всех языков, генеалогическм восходящих к Си. Поэтому в си возвращают 0, что значит, что процедура выполнена успешно.
Использовали сложение делегатов в продакшене. Был парсер документов, который менял состояние в зависимости от положения в документе, и на разных состояниях нужно было обрабатывать токены одним или несколькими анализаторами. Соответственно был словарь состояние-делегат, где делагат не обязательно один анализатор, а может быть несколько с разной логикой. И не то чтобы это было бы сложно сделать без суммирующихся делегатов, но если они уже есть, то почему бы их не заюзать.
Проблемы начинаются если есть взвешенные значения)
Не понятно: как получить все, какое именно ты получил итд.
топ1 проблему прочувствовал буквально на днях, согласен с ее нахождением в топ1
Имхо косяк #1 - это неудобство отписки от ивентов. Особенно с анонимными методами
Мне кажется мы их слишком редко используем последнее время, чтобы это было проблем.
Все меньше и меньше АПИ работают с ивентами.
Спасибо за видос!!!
про 9: Правильным решением будет использование асинхронных методов у IQueryable. У IEnumerable таких нет.
Не совсем так.
WhereAsync, SelectAsync нету и там. Там есть лишь "финализирующие" методы... SingleAsync, ToListAsync итд
Кому как, у меня каждый раз болят дженерики из 3-го места, ну и с ними всё время паровозиком идут массивы из 1-го места. Это массив, список, словарь, как их понимать? И каждый раз либо изобретаешь с нуля, либо тащишь свою библиотеку с решением. Создаёшь в каждой библиотеке модуль Autofac, автоматчески регистрируешь вот это всё... а под капотом боль от "а класс реализует дженерик интерфейс", а "интерфейс наследует этот дженерик интерфейс"? А атрибут? А как из этого безобразия создать инстанцию?... 🤯
Массивы были всего лишь в упоменаниях)
Дуже дякую. Корисне відео. Реквест на налаштування статичних аналізаторів. Тільки в студії, а не в цих ваших джетбрейнах!
А там нащастя як MS став розвивати команд лайн і compiler as a service, воно стало більш-менш універсально, бо працює рівнем нижче за IDE
Проблему с 1 места мне доводилось решать похожим способом, но со временем я понял, что лучше будет сделать отдельный класс RepoMappedEntittiesBase, который собственно и добавляет новый функционал и будет требовать уже для новых наследников передачу нужных данных. Тем самым мы и не нарушаем Open-Closed принцип.
Как по мне оба подхода имеют смысл, т.к. все зависит от контекста того, какие изменения вы планируете внести.
Из всех перечисленных моментов, считаю худшими только с 10 места.
Довольно часто сталкивался с проблемой из 7 места, из-за которой приходилось писать кучу костылей, однако спасибо тебе за её решение, ведь действительно запомнить disposables при подписке упрощает решение этой проблемы.
Немного запутался в обоих Ваших комментариях, если честно.
Обычно с коллегами обсуждаем новые фичи... и часть просто не используем))) чтоб не бесили. Как в шутке - "если вам не нравятся гейпарады, прекратите в них участвовать"
Обычно, если команда большая, то провернуть такое довольно сложно)
Крут!!!
На счёт конструкторов: можно сгенерировать пачку паршл классов в которых определяется конструктор. Либо иньектить зависимости в свойства. Ну а 50 тестов всё ровно придётся изменить.
Насчет свитча орнул) Пока мы - джсеры, страдаем от того что из нашего языка пытаются сделать шарп постоянной копипастой паттернов от туда, то шарписты страдают от того к ним завозят паттерны из джса))) З.Ы. Для тех кто не въехал, в джс свитч кейс как он выглядит в плюсах или хексе это антипаттерн, который заменяют объектом, каждый из свойств которого является кейсом, который стрелочной функцией возвращает результат для кондишена
Хех))
По девятому пункту (IQueryable и IEnumerable): если такая проблема появляется на продакшене, то у вас большие проблемы с тестированием своей работы и язык тут не при чем. Подобные проблемы у вас будет при использовании любого языка
И с одной стороны я согласен, но с другой это лишнее место, где частые ошибки случаются. Язык или API могли быть задизайнены чуть иначе.
Чет так заслушался на вступлении, что показалось, что начала какого-нибудь фильма от Гая Ричи)))
1ое место. Был такой случай на первой самой работе. Был самым зеленым джуном среди сеньоров. И потел каждый раз, когда в процессе багфикса приходилось трогать "то что работает" и было написано опытными программистами. Но в один прекрасный день попался баг, в качестве решения которого, было необходимо добавить в конструктор базового класса некий объект. Как сейчас помню, звоню наставнику и несколько раз уточняю, что "пук среньк надо вот в базу добавить вот это, но там наследование 5> потомков и типа мне надо каждому из них в ктор пробросить этот тип..???" "- Да"....
Дада)
Первое место прямо в самое сердечко.
Где-то пол года выписывал проблемы для этого видео. За это время сам в работе столкнулся с проблемой раза 4
А первая проблема у меня чаще решается практически полным отсутствием наследования, если честно. Если там какая-то общая логика, то её можно вывести в какой-нибудь класс-хелпер.
С наследованием в принципе очень легко отстрелить себе ногу не в одном месте, так в другом.
Была весёлая история, когда переопределили то ли Equals то ли GetHashCode в базовом классе сущности и у нас то ли работа с базой, то ли какие-то вычисления взорвались от этого в совсем другом месте
" у меня чаще решается практически полным отсутствием наследования, если честно." сейчас тебе мамкины архитекторы и мамкины тимлиды накидают г...внеца на вентилятор в комментах )))
А если серьезно, то я согласен, лепить где попало наследование что бы казаться умным бред, его надо применять там где действительно есть общая логика которую копипастить не стоит в классах, но любителей что то поменять в базовом классе всегда можно ткнуть носом в SOLID, а точнее в Open/Closed, но на практике лучше конечно применять наследование там где это РЕАЛЬНО нужно, а не потому что "мы же умные программисты".
Последнее время вообще наблюдаю тенденцию что сейчас код пишется не для решения бизнес задач, а для применения паттернов, архитектур, "умных идей из книжек" и т.д., то есть сейчас не принято думать при написании кода, главное написать код "поумнее" и главное чем сложнее тем лучше, что бы не выглядеть идиотом, потому что забыли про главный принцип программирования KISS, который я считаю поважнее будет чем SOLID и ООП вместе взятые. )))
ну да, ну да, особенно на 10 летних проектах вообще нету наследования, и статиков и классов на 10000 строк)
Ну, там где классы по 10000 строк, там поможет только мучительный рефакторинг или переписывание начисто
Сложные многоуровневые разветвленные иерархии точно могут быть злом и доставлять огромное количество проблем.
Иерархии в 1 уровень, вполне часто могут быть неплохим решением: базовый репозиторий и пару десятков наследников и подобные варианты.
Вот на таких вариантах первая боль и отстреливает часто.
@@DevJungles да, согласен, 1 уровень, максимум 2 это норм. У нас в проекте TL является "архитектурным астронавтом" (загуглите термин кому интересно что это за тип программистов), так вот у нас иерархия в 20-30 бессмысленных классов, черт ногу сломит, а главное что необходимости в этом вообще нет, но у нас в конторе главное написать "код позамудренее" и чем больше он непонятный и сложный тем ты круче.
Так что согласен с @Dev Jungles - Andrii Podkolzin на все сто процентов, 1 уровень вообще идеально, 2 допустимо но нужно точно иметь на это обоснование. 🙂
Хорошее видео, Новый switch case конечно же это ужас. + в последних версиях шарпа очень много сахара, это не очень радует.!
По поводу последней проблемы: в Rider есть удобная функциональность, добавляющая аргументы в функцию, если в одном из мест вызова дописать этот аргумент. Это же применяю и при добавлении нового параметра в базовый класс.
Так же можно через меню рефакторинга это же сделать - в popup окне вписываешь название и типа аргумента, и он появляется во всех наследниках.
Не знаю, как там в VS - давно не использовал
Дада
Есть, иногда даже пользуюсь, иногда забываю.
Но там большой и сложный диалог открывается, пока все прокдикаешь уже злой будешь
Диалог да, а когда фактически изменил конструктор (или любой другой метод), то прям из контекстного меню можно добавить новый параметр во все наследники (или реализации)
Кстати, я про IEnumerable/IQuerible всегда факаплю сам, но в приведенном примере еще большее зло - ToList().
Я даже долгое время думал, что для листа ToList вернет тот же самы инстанц, но в реализации увидел, что всегда создается новый экземпляр
@@DjonNIK777 уточню:
когда прописываешь новый аргумент, он выделяется такой белой рамочкой, и если в этот момент нажать Альт+Энтер или какая у вас комбинация для квик фиксов, то там первым пунктом будет применение рефакторинга
Новый C# это Kotlin, расходимся
На счёт 6го места... default там не впихуемый т.к. в этом случае будет сравнение на значение по умолчанию
С дженериками конечно боль, особенно после котлина где можно сказать "тут у меня интерфейс с неважно каким дженериком IMyInterface".
Да да да!
Я вот примерно это и хотел бы иметь
Третья претензия вообще чистон непонимание. Что будем делать вот тут?
class A : IX, IX
Что тут даст is IX ?
Ну во-первых true))
А во вторых действительно не подумал, что в этом случае вывести тип не выйдет никак(
@@DevJungles Для is вывод и не нужен. Но есть проблемы с тем, что дальше делать с этим знанием и с эффективностью реализации подобного оператора.
А вообще универсальным решением были бы макры или пользовательские расширения языка. Тогда каждый мог бы написать библиотеку своих расширений и жить удовлетворённым.
Есть сорсген, но это не совсем то...
@@DevJungles Совсем не то, точнее будет сказать.
Есть ещё реализация интерфейсов по умолчанию. Когда на это первый раз смотришь вообще мозг ломается
О да!)
Но потом привыкаешь и далее подсаживаешься 😂😂😂
А в целом - да, вроде косяков хватает, но с многими или вообще не сталкиваешься или они лишь чуточку портят жизнь. А потом садишься за Питон или того хуже Джаваскрипт и стреляешь себе в ногу на пустом месте. Например в Питоне свойства объекта в конструкторе надо писать через self., иначе это будут статические свойства. Я разок так выстрелил себе в ногу.
Вон стильно-модно-молодёжный Rust из коробки не имеет yield, пользуйтесь библиотеками :( Подозреваю что если пописать на нём подольше, то тоже можно всякого найти
Немалую часть проблем фиксят линтеры и копайлот.
Лично мне не нравится, что когда у меня есть интерфейс с синхронным методом, я подключаю асинхронную реализацию (хз, например перехожу на новую бд, к которой драйвер предоставляет возможность делать асинхронные вызовы), так вот теперь эти таски пронизывают код полностью. Придётся переписать все вфункции, чтобы они работали с тасками. И это прям вымораживает. Какого чёрта вообще моя бизнес логика должна знать синхронный или асинхронный вызов порта она должна вызывать (например в гексагональной архитектуре)? Бл не должна зависеть от инфра уровня. А тут язык подсовывает явную зависимость.
1. Мне кажется, эти проблемы копайлот не фиксит вообще никак.
2. Вполне реально выстроить слой бизнес логики без тасок и асинхронности. В том примере, что приводите судя по всему бизнес логика вызывает слой данных. Это ок в трехслойке, но не ок в гексагональной. Там бизнес модель в центре и не зависит ни от чего.
Насчет конструкторов вариантов не особо много, средствами языка сейчас, как вариант, только добавлять вторым конструктором, и вызывать из первого с созданием нужного класса. Но если нужно какую-то зависимость протаскивать, то тут без изменений никак не обойтись.
Жаль райдер не умеет сигнатуры всех зависимых конструкторов обновлять.
С языковой конструкцией не хватает какого-то наследования конструкторов, возможно. Так, чтобы если есть класс A с недефолтным конструкторов, то в классе B : A, он сразу был бы доступен, с возможностью переопределения через override.
Так, если бы в наследниках не было бы специализированных конструкторов, тогда кода пришлось бы меньше лопатить.
(Но если в наследниках есть дополнительные параметры, то всё равно фигачить придется)
По-моему тут особо вариантов нет, если появляется необходимость какую-то чувствительную часть кода подергать, то к этому надо быть готовым. Вариант с моделькой с сервисами в приведенном примере считаю перебором. Если у базового класса много зависимостей, которые ещё и часто меняются, и не нужны наследникам, то это скорее всего признаки плохой архитектуры.
По поводу плохой архитектуры согласен, впрочем это не значит, что с такой архитектурой не приходится работать.
11:47 Плачу на этом моменте. Сколько часов было убито в борьбе с этим!
Не понятно,но очень интересно)
Спасибо за очередное полезное!) Никак не пойму как до такого уровня подняться. Ты не рассказывал про свой путь, что надо сделать? Не поступать в вуз, сразу стартапы, куча пет проектов и потом куча ком. проектов, не жениться (чтобы не отвлекали), с друзьями никуда не ходит и пр. Поделись лайф хаком, очень интересно!)
Ахахаха))
Да фиг его: ничего такого вроде не было.
Мне кажется тут сыграло просто время движения: если 10 лет заниматься разработкой постоянно стараясь узнать что-то новое и сделать что-то новое, без экстрима в плане темпа, но и без длинных пропусков, то все сработает)
Отлично, выглядит как план!) Надо будет такой roadmap и транслировать подрастающим разработчикам.👍
Открой для себя Nemerle. В нем в 2006м недостатков было меньше чем в C# в 2023м.
Самое забавное, что я даже знаю это название и даже видел его сорсы и полтора проекта на нем где-то в 18м году)
Но уже тогда, увы, и язык и проекты были заброшены
@@DevJungles А я его до сих пор поддерживаю. 🤫
На счет первого пункта. Если я не ошибаюсь , то можно вместо класса агрегатора и использовать атрибут , наподобие инжект в ангуляр, я недавно натыкался на статью. Суть в том что тебе вообще не надо писать зависимости в наследниках , а лишь указать аттрибут в базовом и он будет применять все di в наследниках
Дада! Есть такое: особенно с модификатором required и get/init свойствами это становится прямо вменяемо.
Классный видос, лайк
У меня проблема, которая на первом месте, была в ts'e, только там в конструкторе было 17 сервисов, в родитель я передавал 10, у того был еще один родитель с 4 параметрами. И к этим четырём параметрам надо было добавить пятый... Энивей у свитч экспрешена первое место у меня
Слушайте, а в райдере (или любой другой IDE от JB) это проблема не решается простым рефакторингом сигнатуры конструктора?
Отличный ролик 👍
К обходу 7-й проблемы в пример можно ещё библиотеку System.Reactive привести. Там тоже логика помещения подписки в IDisposable используется. Также есть механизмы адаптации классических событий. Но, с другой стороны, это уже не Subscriber/Puslisher, а Observable/Observer получается.
Если честно, несколько раз пытался вкатится в System.Reactive, но как-то все время задачи выглядили слишком простыми, не стоящими того, что бы разбираться в этой технологией... ..в общем сложновата она на вход в чем-то.
@@DevJungles Да, сложная вещь, объемная. Я с WPF в основном работаю, там неплохо весьма приживается эта технология. А вот WEB, например, сложно представить, как её использовать.
Разложил сисярп по фактам, так ему 😅👍
По поводу первого места. Мне не понятно, по какой причине нужно постоянно переопределять конструкторы. Этой проблемы могло бы не быть, если бы не пришлось этого делать. При этом они не обязательно что-то делают, они просто перебрасывают параметры и просто существуют. Возможно есть этому объяснение, которое я не знаю и это всё оправдано. Но например в php такого нету, конструктор можно сделать один раз в родителе и иметь кучу наследников, которые будут пользоваться уже существующим конструктором. В тайпскрипте тоже
Согласен.
Кейс, когда конструктор не меняется можно было бы запросто решить.
Более сложный кейс, когда наследники имеют больше зависимостей все равно бы остался, но уже стало бы легче.
Ну. Большая часть это не проблемы языка.
Но с чем согласен, это с войдом. Войд - это реально боль, если тебе нужно библиотеку для команды делать.
Приходится либо одинаковую логику мутить, либо вводить какое-то извращение в духе Unit, Empty, Nothing и всем говорить, что оно есть.
И проблема со вторым подходом, в том что шарп начинает ворнинги сыпать, дескать ты результат не используешь, а ну как сделай Nothing _ = Lib.Execute();
Тема недоделаных дженериков раскрыта не до конца. Стандартные коллеккции не реализуют коваринтность, нет нормальной контрваринтности типа возвращаемого значения в реалилизциях методов базового класса. Нельзя сделать where T is SomeAbstractClass - нужно для SomeAbstractClass делать интерфейс ISomeAbstractClass.
В джаве это реализовано гораздо лучше. Благо на бэкенде магия джеренриков нужна очень редко.
Не соглашусь... Давненько видел джаве, но тогда там дженерики меня прямо ужасали. Там не было generic constaints(может добавили?) и не было возможности сделать Generic от value-type: ArrayList, а только от Integer(который ref-type)
Сравниваю с котлин.
1. Проблема 10 - нету в таком виде, в перекрытых классах нельзя ставить дефолты
2. Проблема 9 - и есть и нет, да можно не то расширение вызвать, но нет идеи query able для внешних вызовов.
Но проблема рантайма зато есть. Проблема есть и даже хуже но не из за языка, а jvm, jar с одинаковыми классами
3. Проблема 8 - нету, Unit (void) это тип
4. Проблема 7. - нету так как благо нет понятия событий
5. Проблема 6. - нету свитч (when) сразу был выражением, и не требуется break, как в golang, по умолчанию брек.
6. Проблема 5. - нету, вывод типов умеет из параметров выводить
7. Проблема 4. - другая, мне совсем не нравится, что в котлин по дефолту паблик, я бы предпочёл приват. Но дефолт тем не менее всюду один
8. Проблема 3. - другая, тоже сложности с апкаст можно сделать, зато рефлексия в куче контекстов не сработает ибо type erasure, генерики мне чуть больше в шарпе нравились, но правда хаки со звёздочкой там не работают
9. Проблема 2. Нету, явное ключевое слово constructor + init. Но в целом я предпочитаю приватные конструкторы и факторки в компаньонах
10. Проблема 1. Такая же. Специфика модели классов которая у джавы и Шарпа почти одна. Поэтому стараюсь меньше наследования, больше композиции, меньше конструкторов, больше фабричных методов
Потрясающий комментарий!
Спасибо за подробный ответ: всего несколько раз смотрел Котлин.
Но он доказывает, что мы не стоим на месте: были ошибки, мы(как индустрия разработки) их исправили.
На то чтобы составить такой же топ по котлину уйдет едва ли не десятилетие и, вероятно, список выйдет менее бесячис
Языки это ещё и рантайм под капотом. Например Kotlin основной и любимый собственно как язык уже 6 лет. Но JVM и джавовская экосистема которая у него под капотом такая мерзкая, что активно для новой разработки переходим на голанг, который как язык по примитивнее, зато всятэкосистема прямо очень хорошая. Не языком единым...
Спасибо
Чем опытней разработчик тем грандиозней ляпы он совершает.
1000%))
про гит в точку
По поводу массивов (их необобщенности) и конструктора - это же следствие истории создания шарпа, как сводного брата Java.
Ага... Но разве оно от этого меньше болит?)
При просмотре подумал что прсто дженерики появились в netFramework 2. И не свящано с историей создания... создатели специально отказались от дженериков?
Половина проблем просто надуманы. А насчёт массивов и генериков и общего кода что мешает использовать интерфейсы? У массива спокойно есть генериковый интерфейс IReadOnlyList`T, например.
Да, есть способы выкрутится.
Но, если работаешь в уровня рефлексии и пишешь какой-то обощенный код, то GetType().IsGenericType нужно помнить, что массивы туда не попадают.
На счёт 8го места. Видел затычку с классом Void с приватном конструктором, а методы с таким типом просто возвращают null
Ага
В MediatR том же для этого есть Unit.Value
30:00 а ещё я в райдере когда копирую файл с классом, вставляю, меняю ему имя, и оно не меняет имя класса внутри! и его приходится менять в двух местах!
Ага
Речь конечно про с# но добавлю по поводу косяков, в entity framework 7 завезли column json, только нельзя чтобы классы в этих колонках имели поле с названием id, будет вылетать ошибка при запросах , так как конфликтует как я понял с ихним десерелизатооом .
Ух.. Не знал этой штуки.
23:20 всмысле 6 модификаторов? а file?))
Да да!)
Забыл его
Вот женерики да, прям иногда хочется хитрых проверок навернуть, и так чтобы "оно само" потом работало при использовании.
А в итоге получаются костыли с кучей наследований.
А на счёт конструкторов - у меня была идея, что если бы на вход можно было передавать какой то класс (или анонимный тип) который может деконструктиться в нужный список аргументов прямо при передаче в конструктор.
var arguments = new SomeClassArgsRecord(a, b, c);
var someClass = new SomeClass(arguments.Deconstruct()); // new SomeClass(a, b, c)
На самом деле: мы все реже пишем оператор new, все чаще объекты создаются контейнром, потому именно передача параметров редко отстреливает прямо так серьезно.
там ещё модификатор доступа file завезли в 11 шарпе)))
Ох.. Точно.
Забыл про него. И он еще и со своим особым контекстом применения)
@@DevJungles спасибо за контент, делаешь крутейшие ролики)
решение на топ1 крутое!
Для топ 1 возможно **args из питона подойдут, но лучше без них))
Я не очень понял про 9 пункт, вроде это же можно запомнить, почти как "жи-ши", не тащи тонны данных через enumerable, юзай силу, Люк.
Ну да, но это нужно постоянно помнить
А ещё легко сделать классный метод для работы с IEnumrrable, а потом "случайно" его заюзать для какого-то Queryable
Знаешь почему синтаксис switch такой? Потому его решили перенести один в один с Rust😂
Андрей, а тебе еще не говорили, что ты реально крут? :)
Спасибо))
Уже лет десять прогаю, даже половины из сказанного не использовал.
"На каждом языке можно писать как на Бейсике!" (ц) кто-то-великий 😆 Оно и к лучшему - проще код - проще сопровождение или портирование.
А можна лінк на оригінальну статтю ? Чи на презентацію, якою ти користуєшся ?
Оригінальної статті не існує, все для цього відео створював ексклюзивно.
Лінку на презентацію та сценарій виклав для спонсорів та патронів каналу.
Хорошее видео, насчёт конструкторов с абстрактными классами полностью согласен, а так же с асинхронными и не асинхронными перегрузками. Как по мне уже давненько пора отказаться от явного применения Task с асинхронными методами и отдать это на вотчину компилятора, т.к. нам, как правило, нужен дженерик объект таска, а не сам таск,🤘
сделай свой обьект или касти внутри как нибудь
С одной стороны согласен, с другой асинхронность тоже важная часть сигнатуры и ее хотелось бы видеть... Не знаю. Не простой вопрос.
@@DevJungles ну, такое, я бы предпочел асинхронные методы суффиксом отмечать, и, возможно, чтобы явный тип был указан при наведении на метод. С var же так работает, почему бы не сделать что-то подобное. А возвращаемый тип, на самом деле не всегда говорит о том, что метод асинхронный, не знаю, доводилось ли ковыряться в либах к sql различным, там можно часто встретить методы, которые возвращают таск, но при этом прям параметром принимаю булевый флаг async и если ты выбрал false, то у тебя будет синхронный метод беспантово завернут в таск, такое..
Первое место жиза, но только не понятно как настроить его для DI. Есть видосы на эту тему? Еще тоже бесит что он автоматом не указывает дженерик, я думаю это могут добавить IDEшках как авто заполнение.
Гугл: YourDiContainerName property injection
Смотря каким DI пользуетесь. В MicrosoftDI Add(Singleton/Scoped/Transient)(provider=>/*тут выбираете из провайдера что надо и конструируете экземпляр класса*/)... посмотрите повнимательнее методы регистрации типов в вашем DI должно быть что-то похожее
Сделаю видео где-то в мае)
А як же record class, який може бути positional (immutable) або звичайним (mutable). Здавалося б, навіщо?
Тут може щось упустив, бо не зрозумів про що це(
@@DevJungles positional record це такий: public record MyRecord(string Var1, int Var2);
Він генерується у клас з конструктором, з додатковою логікою на порівняння типів.
Звичайний record наступний:
public record MyRecord
{
public string Var1 { get; init; }
...
}
Він генерується як звичайний клас, взагалі без нічого. Тож незрозуміло, який в ньому сенс.
}
Зачем слайды на английском? %)
И вы похоже упорный девелопер - спеллчекер пытался направить на путь истинный, подсвечивая "Finilizer", но не смог победить :D Ну и "mension" тоже
С mension, если честно, прямо стыдно стало, когда допер(
Тоже отмечу жуткую боль от дженериков. Они вроде бы неплохие, но не хватает кучи инструментов (по типу усилений ограничений по иерархии интерфейсов, например, и других).
Тему самой главной проблемы (№1), честно говоря, не очень понял. Как изначальную проблему (наследование реализаций в программировании бьёт по рукам ещё со времён множественного наследования), так и решение-костыль (оно же усложняет чтение класса и параметров конструктора). Не совсем понимаю контекста, но разве использование Type Object паттерна не излечит от этой проблемы? Или декоратор, опять же.
Если честно не вижу как декоратор или тайп объект может решить эту проблему...
@@DevJungles нет наследования - нет проблем протягивания зависимостей через всю иерархию. Что-то в этом роде))
Но это, наверное, я просто изначальную проблему не так понял.
Синтаксис свича, кстати, реально противный и не очень понятно почему его сделали таким. Я его до сих пор толком не помню и приходится гуглить
его делали так по аналогии с F#, но там он добрый и ласковый друг, а здесь какой-то огузок, но выручал меня пару раз в связкой с ValueTupple может отлично заходить в некоторых ситуациях.
Занимался написанием компилятора для API одной системы... И пришла мысль что просто так проще компилятор реализовать, отличается порядок лексем. Отлично же, меньше возвратов назад делать, быстрее компиляция
Проблема №1 реально боль, но предложенное решение - это антипаттерн "God object". Нет, это не решение) Продолжаю страдать...
Почему God Object? Это же по объекту аргументов на каждый класс.
Единственная задача: поддерживать аргументы.
@@DevJungles Наверное потому, что у меня был опыт работы на одном из проектов с таким объектом , который содержал в себе сотню полей, всегда частично заполненных, и ты никогда не знал, какие именно будут заполнены. Это был ад адский. Видимо, я получил психологическую травму, от которой так и не оправился)))
Снизить поражающий эффект этого антипаттерна можно, если все поля будут обязательны, инициализироваться через конструктор, рид онли. Тогда, по крайней мере, ты будешь знать, глядя на этот, тасказать, ДТО, что от него ожидать.
А что делать, если у тебя сложная иехархия базовых (абстрактных) классов (а у меня на текущем проекте именно так), делать еще один gob? и сколько их будет?
В целом это системная проблема, которая говорит о том, что что-то сильно не так в королевстве Датском
Для себя решил, что лучше терпеть эту боль, но следовать шаблону DI
Я может не слишком хорошо сформулировал идею...
Идея для каждого класса с множеством аргументов в конструкторе делать отдельный класс аргументов.
Если есть иерархия, то повторять ее в этих классах аргументах.
Все отмечать required { get;init; }
Здесь в самом конце видео еще раз к этому возвращаюсь как раз: ruclips.net/video/euZvNDFwTQ8/видео.html
спасибі за роботу. А можна приклад як навчити DI створювати об'єкти як в топовій проблемі 🙃🙃🤗🤗
Буде відео.
У травні, сподіваюсь)
я таки вырезал кусок про войд😁
Для использования на закрытых лекциях по философии?)))
@@DevJungles ага, послал друзьям философам, выразили респект )
по поводу конструкторов -- наследование зло))
даже если ты и заюзаешь предложенный воркараунд, это не решает главную (и по факту единственную) проблему -- ты всё равно поменял поведение 50ти наследников, только теперь ты об этом скромно умалчиваешь в своём PR. не надо так делать.
если зависимость какая-то опциональная, то можно поменять абстрактный класс на интерфейс с дефолтными реализациями и туда докинуть приватный геттер с дефолтной реализацией возвращать какой-нибудь стаб или нулл, а дальше уже по контексту.
энивэй, сама по себе проблема больная, но она больная не просто так.
В целом согласен, но...
Но у нас уже есть очень много способов более серьезно изменить поведение наследников и не сообщить об этом в PR: изменение тела базового виртуального метода, например.
А вот опциональные зависимости меня обычно тоже довольно сильно пугают...
В этих случаях мне скорее хочется решить это:
- в идеале композицией
- хотя бы наследованием.
тоже с топ 1 не согласен, предлооженное решение еще и приведет к созданию GodObject с кучей всякой нужной и не очень херней внутри, и там зависимости и использование вообще хер найдешь. у меня в одном рефаторинге пришлось поменять по этой схеме много конструкторов, но только благодаря их явному изменению Я смог заметить другую прблему, что в многие классы добавлялись вообще не нужные им зависимости просто потому что этого требовал базовый класс. в итоге было найдено кривое наследование.
Не понял как это приведет к созданию God Object.
Кривые иерархии действительно бывают, но обычно их и видно из далека)
@@DevJungles может привести если начнут пихать в класс все подряд, типа "удобно же" и тогда есть шанс появления класа который будет в себе содержать резолв всех зависимостей и его будут пихать везде. тогда может быть уже сложно понять какие из зависимостей где используются. по сути получится внедрение репозитория зависимостей
зачем писать два минуса? --
Я нашёл для себя, что 3 "самых худших момента", мне были уже знакомы и я так или иначе с ними сталкивался 🤯
Ну и остальные по мелочи знакомы были, хы.
Я шо профи?
Именно!
Добро пожаловать в клуб)
@@DevJungles но я начал в c# первые шаги(до этого другие языки были) делать всего чуть больше года назад😅
Відносно першого місця, можна засетити дефолтом значення в null:
(SomeType sometype, IMapper mapper = null)
Але ж він тоді для спадників так і залишиться null(
А можно топ1 решить аннотациями, например добавить property в базовый класс с аннотацией [Inject]. Если что, я не настоящий волшебник....
Да, есть что-то вроде этого.
В целом со свойствами get/init и модификатором required получается достаточно хорошо.
Объяснение 9 невнятное, запутанное и не объясняет происходящее. А так видос зачётный.
Может не слишком хорошо объяснил где-то местами...
@@DevJungles В данном примере переполнения памяти не будет. Данные не будут все ЗАкачаны в память, а будут ПРОкачены через локальный компьютер, но при этом фильтр хоть и локально, но работать будет. А вот если бы в примере был OrderBy или GroupBy, то все описанные страшилки случились бы.
я думав, що я один такий не можу запам'ятати switch expression)
Нііі) Нас таких купа
@@DevJungles да сих пор без подсказок среды не напишу. Но не согласен и с нападками, и представленными альтернативами. Дело в том что, switch как выражение, может, много больше по сопоставлению кейсов чем стандартный switch, и поэтому варианты из других языков не кажутся вдохновляющими. И естественно он должен вернуть что-то , когда ничего не совпало. Почему там _ , а не default -- загадка.
25:48 думаю кто можно было отойти, создав ещё один пустой не дженерик интерфейс, и наследовать его от дженерикового, и уже в is проверять на не дженериковий интерфейс
Да да
Я это и предлагаю в варианте обхода, но оно проблему полностью не решает, если нужно работать с объектом дальше
40 минут видео только для того, чтобы словесно описать 2-страничный документ? Куда же катится человечество....
Если ограничение не время, а пропускная способность головы, то это может быть небольшой проблемой)
Теперь вопрос: так ли мы уверены, что нас ограничивает время?
Что бывает чаще: в конце рабочего дня вы хорошо поработали, есть ещё много дел, вы бороды и веселы, но уже 19:00 и пора домой.
Или
17:00 и лузится стекающая изо рта от тупняка начала капать на стол))
Еще после котлина раздражает то что все по-умолчанию мутабельно.
И I в названиях интерфейсов, которое по началу кажется здравой идеей на деле полная ерунда, ну больше выбешивает конечно IReadOnly, это просто супербойлерплеейт, ведь для хорошего тона все должно быть ридонли, в итоге у тебя весь код с полями засран этими IReadOnly... .
Как по-мне это самая серьезная ошибка, которая говнит весь clr, и никакой новый язык (аля котлин для clr) это уже не исправит в отличии от других ошибок из видео
Ну еще отсутсвие var в полях, который тоже очень сильно бойлерплейтит в некоторый случаях.
А еще async/await после котлина это просто ужас, просто не хочется возвращаться в c# когда вспоминаешь о них и о cancellationToken, крайне недальновидное решение которое язык подпортило, конечно их можно оживить и адаптировать если сделать новый clr язык правда с костылями.
По мутабельности соглашусь, даже хотел поставить в видео, но потом решил не делать: много про это сказано и, пожалуй, это дань тому как тогда видели парадигму.
А можешь подробнее сказать про cancellationToken и async/await? Что с ними не так?
@@DevJungles 1) Синтастический бойлерплейт постоянно надо писать Task и await хотя на деле для компилятора в этом нет необходимости все и так понятно. 2) Сложно провести аналог, ведь корутины это немного другое, но вообщем в котлине с канселяцией все проще ибо этот токен уже вшит в корутину и есть дочернии опреации которые канселятся вместе с родителем и следовательно не нужно постоянно прокидывать и комбинировать эти кансел токены и следить за этим.
можете показать 1 и 2? очень интересно, но пока не понятно.
Из относительно новинок ещё можно вспомнить проверку на null через is (pattern matching), а не ==
Способ через is рекомендуется самим майкрософтом, чтоб случайно не попасть на переопределение оператора ==
И всё было бы хорошо, если б этот is работал везде. На текущий момент его нельзя использовать в expression-ах (в отличие от лямбд, что дико путает). Т.е. во всех linq-выражениях на is будет ругаться...
По итогу в коде получается сборная солянка, а ты постоянно натыкаешься на ошибку от IDE в linq... 🤬
"чтоб случайно не попасть на переопределение оператора ==" - какой идиот придумал обходить переопределение операторов?!!? Их СПЕЦИАЛЬНО ПРИДУМАЛИ, чтобы глупый канпелятор знал, как ПРАВИЛЬНО сравнивать объекты.
Я уже в одном видео хорошенько прошёлся по паттерн матчингу как раз))
"Современные" языки не проще, они еще просто не столкнулись с теми проблемами, с которыми уже десяток лет сталкиваются разрабы. Ваше утверждение как слова Бендера из футурамы про казино.
Да.
Главная проблема в этом плане: просуществовать в мейнстриме лет 20-30
погоди, а если базовый класс унаследовать от типа передаваемого аргумента и не передавать этот аргумент...
как вариант... не утверждаю...
Базовый класс от типа передаваемого аргумента?..
Звучит как что-то очень странное))
@@DevJungles да, туплю - это интерфейсы, их реализовывать придется. Хотя, можно, протыкать - может, там - просто... Найти реализацию IMapper и от нее унаследоваться... Хз, мысль, просто пришла...
Я конечно понимаю прикол 42, но возвращайти пожалуйста просто дефолт в таких случаях.
Это же демки, а не продакшн код)
@@DevJungles знаю я как демки потом лежат по 10 лет в продакшене 😂
У нас в каких то тулзах в качестве успешного кода возвращается год рождения автора.
Ну что, опять НЛП в действии, позавчера ревьювил проект где ребята как раз Queryable передали в метод который принимает IEnumerable:)
Я очень плотно влез в реальность 🤪🤪🤪😜
Занудничаю: «упоминание»: Mention, не Mension
Вот это я опозорился(
При том из-за того что есть слово mension(усадьба, поместье), то мне даже орфочеккер не ругнулся 😔😔😔
Спасибо за замечание!
@@DevJungles а поместье - Mansion 😁