Как стать автором
Обновить

Комментарии 131

Не такая большая дичь существование этих странностей, как если кому-то взбредёт в голову спрашивать их на собеседовании

на свинге левая скобочка туда же))))) это что-то из разряда фишка-дич ) (по шифту если вот уже просматривается - shift+9 )))) вроде логично, но там дальше веселее ))

там такие дебри, понятно что можно сделать рабочим, но на первом приближении можно и удалять левую скобочку при выделении текста и вставке двух скобочек )

тоесть чтобы при shift+9 вставляла 1 скобочку, а при выделенном тексте shift+9 обрамляла в скобочки )) это задачка на свинге просто епик )

Не такая большая дичь 

каждый день используете?

Нет, просто постоянно сталкиваюсь с тем, что что в мире вообще много нелогичного, непродуманного и просто странного. Так что уже как-то не сильно удивляет

Большинство примеров напомнило

<******> к вопросу о вчерашних скриптостраданиях. Только что кодер знакомый прислал, нашёл в коде программы, написанной уволенным коллегой незадолго до ухода:

<******> #define TRUE FALSE //счастливой отладки суки

* ****** такого извращённого юмора ещё не встречал

https://башорг.рф/quote/268036

Это особенность дотнета - очень много чего можно переопределить с помощью особой кодовой магии. В Java такого нет.

Можно != нужно

Дичь нулевая - называть дичью то, чего автор либо не понимает, либо тупо стебётся на серьёзных щщях.

PS: третье -- красиво. Остальное - смех гопников над прохожими.

Мне ещё с операторами понравилось - довольно изящно получилось.

несколько лет назад подобные статьи было модно писать про яваскриптик. теперь гопники и до шарпа добрались.

Похоже автор толсто троллит. Всё приведённое - чистый детсад, любой джун знает.

Да и автор похоже лютый виндузятник, раз не смог собрать .NET runtime вручную из гита в Linux-e, а сел на толстую иглу дядюшки Билла. Собирать, дядя Фёдор, нужно было отсюда https://github.com/steveharter/dotnet_coreclr/tree/master/Documentation

А так сплошная вода, даже не показал, как отключать AVX инструкции для JIT компилятора в среде CLR.

Где дичь то? В основном фичи языка используемые не по назначению и подавляющему большинству в реальных проектах никогда не встретятся.

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

самое крутое переводить в рантайме через байткод флоат в инт или инт во флоат )

там уже не просто приведение типов там жесть ) юзинги какие-то ) какая-то магия )

ну или нет, но помню видел пример забыл зачем там это делалось

Еще один, не менее отбитый вариант с ручным вызовом приватного конструктора:

Ничего особенного. Это стандартная возможность Reflection!

каждый пример — повод к немедленному увольнению

Или более оптимистичный вариант - повод к немедленной записи автора такого кода на дорогие курсы по языку за счёт фирмы)

Но я бы, наверное, уволил)

Хотя я до сих пор не понимаю — как и чем может быть опасен для человека язык программирования.

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

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

К первому примеру можно добавить lisp и ocaml
А второй похоже rust
😂

В Java через рефлексию тоже можно получить доступ к закрытым полям и методам класса

я как-то смотрел какой-то обзор там рассказывалось как можно защищенные поля классов из виртуальной таблицы в С++ цеплять )

я тогда не знал зачем это и сейчас не знаю

защищенные поля классов

C++

#define private public
#define protected public
#include "someclass.h"

пошла жара ))

у вас тут, так как я не пользуюсь этим какие-то определения в # , человек показывал через адреса )

один раз на хабре мне попалась статья, где разработчику надо было кучу объектов С++ обрабатывать в условиях ограниченного времени, так он использовал хардкодные смещения в асмовом коде, чтобы вытаскивать конкретные поля для этой обработки. То есть один из вариантов "для ускорения, когда по-другому никак".

Дичь четвертая: упоротый async await

Обратная совместимость. Словаasync, await могут быть как ключевыми, так и идентификаторами в зависимости от контекста. Они были введены не с первой версии дотнета, поэтому если бы их сделали жестко ключевыми, то какой-то старый код мог бы перестать компилировать, о чем разработчики языка конечно же подумали, в отличие от автора статьи (если весь этот пост не шутка).

Дичь пятая: вызов без инициализации

Даже из названия используемых классов становится понятно, для чего нужна такая функциональность: для оптимизации десериализации, чтобы не инициализировать поля, значения которых сразу же будет переписано. Использовать можно, если точно знать зачем это нужно.

Дичь шестая: угар по гречески

Здесь вообще претензия непонятна и подобное почти везде написать можно. Если хочется запретить нелатинские буквы, то можно настроить какой-нибудь линтер. Тем более с unsafe.

Дичь восьмая: строка, которая хотела стать числом

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

Дичь седьмая: рефлексия без рефлексии

Возможность творить такое без использования спецального API для рефлексии — неожиданный сюрприз для анализаторов кода и антивирусов.

Объединения - это фича языка, которая используется при написании быстрого кода. Например, когда нужно получить побитовое представление float и изменить единичные биты. И обнаружить такое несложно, учитывая вот это:

[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct Union
{
    [FieldOffset(0)] public Alpha AsAlpha;
    [FieldOffset(0)] public Bravo AsBravo;
}

Как говорится комментарии излишни, отмечу лишь что работа с типами double и float чаще всего происходит в геймдеве и каждая подобная ошибка может стоить неделю отладки «в мыле и с красными глазами».

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

А если серьезно, просто не надо использовать такой код в реальных проектах, благо что каждый пример — повод к немедленному увольнению.

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

В греческом угаре unsafe нужен исключительно для указателя в делегате, а не для греков.

Слушайте, я честно не знаю что на такое ответить.

Под каждым таким примером есть ссылки на авторов, которые собственно и обнаружили аномалию — в основном это разработчики компиляторов, средств разработки, авторы книг по C# и PhD.

Если член команды Jetbrains ReSharper пишет что нашел дичь (ну ок - нестандартное или необычное поведение), наверное он чего-то в этом понимает.

Не находите?

История с «рефлексией без рефлексии» недавно всплыла у ИБ‑специалистов, в качестве новой техники, которую использует малварь для обхода антивирусов.

Как можно с такой звериной серьезностью защищать и оправдывать откровенную дичь — не понимаю.

Слушайте, я честно не знаю что на такое ответить.

Что некоторые (многие?) примеры действительно слегка притянуты за уши и тянут на одну-две совы максимум. Не «отбитая дичь», а ожидаемое поведение.

Одна-две совы?

Не в первый раз вижу такой критерий упоротости. Не подскажете, на что это референс?

Явно речь про "натянуть сову на глобус"

Ссылки на авторов которые не знают про боксинг/анбоксинг и AsReadOnly(), например.

Члены команды ReSharper до сих пор не смогли заставить Rider нормально дебажить контейнеры, особенно докер компоуз, с чем студия справляется давно и успешно.

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

И до сих пор нельзя нормально поставить новую версию IDE не превратив %ProgramFiles%\JetBrains в помойку. По итогу там появляется 100500 версий приложения и сильно не факт, что версия которую ты запускаешь из быстропоиска в меню Пуск - последняя (после чего некоторые штуки, например биндинги к БД, превращаются в фарш и приходится скакать с бубном как починить). Но иногда бывает ещё интереснее

Ещё клёвый ToolBoxкоторый не может обновлять IDE-шки потому что прав админа не хватает и попросить - ну никак. При этом обновление из самих IDE работает... если не стоит этот самый Toolbox...

И вишенкой на торте всякие опусы уберпупероптимизации после которых то рендер не работает, то шрифты криво рендерятся потому что два монитора (ноутбкук у которого 125% скейл на своём мониторе и 100% скейл на подключаемом), то вот такое вот https://youtrack.jetbrains.com/issue/RIDER-111865/Problems-with-several-instances-of-IDEs-Rider-and-WebStorm-on-linux

Я уверен, там если покопаться - можно реальных опусов найти, а не вот этих альтернативных видений вполне себе стандартных фич

@alex0x08 Нет, не займусь, у меня хватает более интересных задач, но могу ссылку на доки по C# скинуть, где, например, приватные конструкторы отлично описаны

Автор в Jetbrains не работает, но обязательно передаст коллегам )

Я уверен, там если покопаться - можно реальных опусов найти,

Займетесь?

Оффтопик конечно.

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

Почему тулбокс не мог бы просто устанавливать в обычном режиме.

Тонкости языка, отладка и IDE - не особо связанные вещи. Этим даже разные команды занимаются.

Это в целом не претензия к JB, т.к. я активно пользуюсь их продуктами и там каждый цент за all products pack стоит того. Особенно DataGrip. Поинт в том, что автору мерещится (и он агрессивно это вот прям выпячивает в каждом втором комменте) какая-то ультимативно превосходящая экспертиза у кого-то в там, которая позволяет плевать на официальную документацию и вещи, которые буквально в каждом втором учебнике прописаны. Откуда такая потрясающая нерушимая божественная экспертиза, что она должна шатать штатные, чётко прописанные, обжёванные со всех сторон всеми кому не лень, появившиеся не просто так и вполне себе обоснованные механизмы - пока не понятно. Примера "а должно быть так-то и так-то, потому что иначе вы имеете вот такие и вот такие грабли в каждом проекте" - нет. Супер-пупер ультимативного всеподавляющего качества уровня сверхчеловека тоже пока не заметно. Пока выглядит что автор нахватался каких-то опусов по бложикам и пытается давить на непонятно что

там с работой окна в пространстве где есть Н ориентированных мониторов(славо богу я не сталкивался покачто с мониторами, (сужу по винапи, там реально сложновато войти сразу в пространство Н мониторов) с размерами, уверен танцы не только на вашем слое, но и в разработке, это поидее java и swing, это вашу проблему не решает, но там реально гемор

Как можно с такой звериной серьезностью

Безотносительно данной ветки. Мне иногда кажется, что почти каждая ваша статья раскрывает какое-то "особенное" чувство юмора у отдельных пользователей. Если бы вы явно не указывали что "так делать не надо", то вообще Адъ и Палестина наступили бы с требованием извинений.

Ну меня тоже пугает что откровенную дичь считают нормой и пытаются объяснить, я был более хорошего мнения о .NET-разработчиках )

Если член команды Jetbrains ReSharper пишет что нашел дичь (ну ок - нестандартное или необычное поведение), наверное он чего-то в этом понимает.

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

Мне кажется ссылаться на сотрудников Jetbrains, компании

Ну хорошо, еще один из авторов работает над диалектом Scheme, а другой - создатель VsVim. Тоже недостаточно авторитетно?

текстовый редактор сделать не проблема на свинге ) в условиях java - я доделал по оперативке уже 100-350, базовый функционал тоже есть )

Мне кажется ссылаться на сотрудников Jetbrains, компании, продукты которой хоть славятся своей функциональностью но не менее знамениты тотально хреновой оптимизацией, такое себе.

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

Для начала пусть научаться текстовые редакторы писать которые половину оперативки не съедают

Можно выключить все свистоперделки и увидеть, как IDE заработает куда шустрее. За одним станет понятно, какие именно фичи требовали столько производительности.

Не оправдываю JB, но, имхо, их можно простить за это, если учесть навороченность их продуктов.

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

Никому не нужен идеальный код. Нужен хороший продукт. А смотря на тот же Rider у меня не возникает в голове мысли - эти ребята отлично знают C# и выжимают из него все.

А смотря на тот же Rider у меня не возникает в голове мысли - эти ребята отлично знают C# и выжимают из него все.

Окей, что же тогда является для вас таким эталоном?

У меня возникает, уже 5+ лет как перешёл на райдер со студии.

Если член команды Jetbrains ReSharper пишет что нашел дичь (ну ок - нестандартное или необычное поведение), наверное он чего-то в этом понимает.

Так-то я тоже в JetBrains работаю, правда над компилятором Котлина (в котором тоже можно написать много подобного и похуже). Но наверное из-за этого я понимаю причины подобного поведения в большинстве приведенных примерах. Над запрещением некоторых можно подумать, но все это сложно с учетом того, что нужно сохранять обратную совместимость.

Безопасность типов в языке это не защита от злонамеренного вредительства, а лишь дополнительное средство контроля ошибок. Ни один из примеров невозможно воспроизвести случайно в рамках повседневной разработки. Даже этот несчастный ref параметр. Вообще, этот пример - баг компилятора, который позволял делегат (ref x) => ... присвоить переменной типа (in x) => ..., что неправильно по сути, приводило к некорректному поведению и было соответственно пофикшено после баг репорта. У вас не может быть объективной причины для подобного каста. Да и в принципе нужна веская причина, чтобы сделать параметр ref, и чтобы иметь такую причину, нужно хорошо понимать как он работает, и почему он у вас ref, а не ref readonly, не scoped ref и не scoped ref readonly. Если вам необходимо изменить значение static readonly поля (да, в примере не константа, константу изменить не выйдет в принципе) - вы можете это сделать и без бага: через рефлексию. Зачем это вам может понадобиться - вопрос отдельный, но возможность есть и всегда была. И сделать это можно только намеренно, хорошо понимая что вы делаете.

Многие фичи, позволяющие "творить всякую дичь" создавались для хардкорной оптимизации, кодогенерации, анализаторов кода, создания нативных AOT сборок, кастомизации работы компилятора и других узкопрофильных, редких, но в отдельных случаях очень полезных вещей. Чтобы понять зачем и почему та, или иная дичь имеет место быть, можно пойти на гитхаб в репы МС и либо почитать обсуждение конкретной фичи самими разрабрами, либо поискать примеры использования в коде. Благо там все нынче опенсурс, вплоть до митинг ноутс. Я на 100% уверен, найдете рациональное зерно, либо баг репорт для любой дичи. И, к слову, ребята из МС, которые пилят сдк, рослин и все остальное, точно не уступают в квалификации джетбрейновцам. Конечно, если это не ваш профиль, тратить время на это вам вряд ли захочется.

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

Спасибо про уточнение насчет константы. Всем же известно, что она хардкодится и заменить ее только дллкой получится. Но поленился проверять, как там объявлен сепаратор, подивился, но поверил автору на слово, что возможно все, особенно в свете остальных примеров) А ведь это показывает, насколько автор ответственно подошел к теме.

Не особо понял про константы и длл. Вся "прелесть" констант в том что компилятор явно прописывает значения в коде без каких либо переменных. И это приводит к "нежданчикам" когда в либе сначала одно значение, на его основе компилируется код использующий константу, далее либа заменяется на новую путём простой замены файла с иным значением и вуаля -- программа работает неправильно. Поэтому выносить в константы всё подряд не ок.

Дичь вторая: изменяемый «read only»

А дичь-то в чём? Вы привели List<T> к IReadOnlyList<T>, который и правда реализует этот интерфейс, и не только его.
Если бы вы привели список к IReadOnlyList<T> по уму через List<T>.AsReadOnly(), то у вас бы такой трюк уже не прокатил бы.

Специально процитирую из приведенного разбора на StackOverflow:

IReadOnlyList<string>, despite the name, just means "list you can read", it doesn't mean "immutable list". Whether the list is immutable depends on the concrete class that will end up implementing the interface.

И «despite the name» тут ключевое.

IReadOnlyList<string> вообще-то не является классом! Это интерфейс.

И этот интерфейс означает "объект, который имплементирует этот интерфейс, можно использовать как read-only list".

Не больше, не меньше. Никто не обещает, что этот обьект не могут изменять в другом меньше.

А что касается приведения ((List<int>)readonlyList) - в реальной функции, принимающей IReadOnlyList, это запросто может не сработать, потому что вообще-то IReadOnlyList может имплементировать любой другой класс.

В Java не то же самое с unmodifiable коллекциями разве? В том плане, что можно грязно скастить и добавить новый элемент

В том и дело что нет, в Java unmodifiable это именно реализация, которая будет выбрасывать исключение на попытку изменения такой коллекции. Железно и надежно.

Ну так берите ImmutableList, он не исключениями кидаться будет а просто интерфейсов даже подразумевающих именение не реализует. Ну или враппер в виде ReadOnlyCollection. Будет как в Java.

Да, перепутал с Kotlin. В нем как раз-таки read-only коллекцию можно скастить к мутабельной и добавить элемент. Как в вашем примере на C#.

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

List<int> list = [1,2,3]; //создали лист
var roCollection = list.AsReadOnly(); //получили экземпляр ReadOnlyCollection

ReadOnlyCollection никакой злостный вредитель к листу обратно не приведет и она тоже реализует IReadOnlyList который нам нужен. Конечно, никто не помешает потенциальному вредителю изменить коллекцию через рефлексию, бесовщину с указателями, или просто-напросто создать из нее лист, добавить туда значение и переприсвоить без всякой магии в одну строку, вот так:

List<int> numbers = [1, 2, 3]; //создали лист
var roNumbers = numbers.AsReadOnly(); //получили ридонли коллекцию

roNumbers = new ReadOnlyCollection<int>([.. roNumbers, 4]); //фигвам

Если кому-то надо накосячить, компилятор не поможет.

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

Каст типов туда-сюда не меняет базовый объект, List<T> от каста к IReadOnlyList<T> этого резко не перестанет быть List<T>. Ошибки в поведении нет.

По второму примеру вообще мимо: показано явное приведение, которое явно задекларировано в ЯП, повсеместно используется и при этом автор говорит что это нехорошо. Ну ок, автору виднее. =)

Хотя тут вся статья в духе Wat, чисто посмеяться -- норм. Но на практике конкретно эти вещи не шибко мешают, а вот например непонимания как работают константы может подкинуть реальных сюрпризов на практике.

Ну ок, автору виднее. =) 

Автор всего лишь все это собрал в одну статью, нашли и описали как "weird" более опытные в C# камрады, которым действительно виднее - под каждым примером ссылка на источник.

Ну, стоит хоть немного понимать что ретранслируешь. Пример с IReadOnlyList ну прям вообще ни о чём (мягко говоря) и кажись даже у новичков нет проблем с пониманием как работает приведение типов. Выше уже упоминали что interface != class, и такое явное приведение допускается исходя из логики что разработчику известно больше, чем компилятору. Более того, со временем в C# добавили такую возможность как после проверки на тип сразу привести к нему в одну строку, например if(variable is MyType myType) { /* использование переменной myType */ } что как бы намекает.

Почти все примеры - "Когда программист творит дичь на C#", а не дичь C#.

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

using MyLib.Seconds;
await 5; //ждём 5 секунд

using MyLib.Hours;
await 5; //ждём 5 часов

Всё за счёт того, что GetAwaiter может быть методом расширения.

Тоже кстати вполне себе вариант для упоротой коллекции. Особенно если спрятать среди гор кода в большом проекте.

И кто виноват? c#? А есть вообще язык, на котором можно только правильные вещи делать? А что такое правильные? А пользоваться в широком спектре им можно будет?

При желании, можно попытаться устроить supply chain attack через пакеты с библиотеками, но будет ли это проблемой языка? Сомневаюсь.

Лучше бы серьёзную статью про минусы дотнета (если такие ещё остались).

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

А что с обработкой ошибок не так? Есть исключения, где надо есть флаги возврата. У ловли исключений синтаксис удобный, можешь ловить как хочешь, с логированием интеграция, в платформах есть глобальные обработчики. Единственный минус, который в голову приходит - нет стандартного способа добавлять в исключения доп. данные (но это не значит, что вообще нет способов).

Проблема в том, что ты не знаешь какие исключения выкинет метод, пока не загуглишь/посмотришь код/выяснишь в рантайме, по факту можно на уровне синтаксиса это обозначить

Данные можно передавать, просто кастомное исключение с со своими полями какие нужно

Исключения не являются частью контракта метода. Более того, разные реализации одной и той же абстракции могут иметь разные наборы поднимаемых исключений - что с этим делать прикажете? Как только исключения становятся частью полиморфного контракта - он больше не полиморфный.

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

1) а я хочу чтобы являлось

2) не вижу проблемы выносить исключения в абстракцию, и с полиморфизмом ничего не случится

Вы пытаетесь спорить с реальностью, исключения в контрактах УЖЕ существуют, я лишь говорю, что такго не хватает в C#, посмотрите как это сделано в java

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

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

когда писал на джаве, кроме вопроса "зачем?!" по их поводу у меня не было. хорошо, что у авторов шарпа возник такой же вопрос :)

Подскажите, а как быть, когда функция внутри вызывает универсальный Action? Если где-то и можно сузить круг вероятных эксепшенов через дизайн кода, то как например быть с условными классами-диспетчерами, чья задача - выполнить лямбду в другом потоке? Тут принципиально невозможно охватить все случаи

Я слабо представляю как это должно выглядеть. Самое банальное -- метод принимает делегат обратного вызова, который может выкинуть своё уникальное исключение.

Ну, камон ДЕЛЕГАТ, он как раз определяет сигнатуру метода (контракт), ничего не мешает там же определять исключения

Я не знаком с java, поэтому нубский вопрос -- без throws в описании метода можно вызвать throw? Если да, то выглядит как прикольная фича, но в c# можно указать исключения в summary.

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

Если это не рантайм исключение (не описывать же в каждом методе, что он может аут оф мемори кинуть), то описывать обязательно.
Проблема в том, что этот подход (проверяемые исключения) все ненавидят.

Имхо, исключения как раз таки тот единственный механизм который помогает тебе прокидывать информацию вверх по стеку, если текущий метод не знает, что с этим делать, то наверху разберутся. По хорошему, каждый метод ОБЯЗАН явным образом так или иначе обработать исключение, прокинуть наверх, логирование, или метод сам разбирается с исключением, если может.

Вообще говоря обязан.

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

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

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

(вероятно, вы имели в виду "Вообще говоря не обязан")

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

Ну "я хочу" и "нерешенная проблема" все же не совсем одно и то же)

А чем не подходит xml документация?

Вот так
Вот так

Не знаю как это выглядит в джаве.

"не решенная проблема", ну имхо, найдутся ещё люди которые бы такое хотели. И эта фича прекрасно ложится в dotnet. Думаю, глупо отрицать ее полезность, это не мной придумано.

xml-дока выглядит похоже на то, что нужно, на она имеет "рекомендательный" эффект. На уровне синтаксиса компилятор будет кидать ошибку. И нет варианта не обработать исключение.

>Единственный минус, который в голову приходит - нет стандартного способа добавлять в исключения доп. данные
Почему нет Exception.Data Property (System) | Microsoft Learn ?

Да, оно есть, но это не особо стандартная (имею в виду общепринятая) вещь. Напр., в последний раз когда с этим сталкивался, Data не поддерживалось в то ли логгере, то ли ещё какой-то библиотеке для исключений.

Дичь пятая: вызов без инициализации

Кейс из жизни. Приватный дефолтный конструктор - довольно распространенная практика, например, для Ddd. А как в таком случае таким инструментам как entiti framework или linq to db создавать экземпляры ваших сущностей? Именно FormatterServices.GetUninitializedObject, кажется, используется ими под капотом для этих целей.

А как в таком случае таким инструментам как entiti framework или linq to db создавать экземпляры ваших сущностей? 

Не поверите: никак.

Такие вещи должны регулироваться соглашениями на такие сущности, как это сделано в Java и JPA например.

С чего Вы решили, что должны и с чего решили, что так как сделано в Java и JPA это правильно? Что вообще такое правильно, в данном контексте?

Зачем скриншоты с обоями рабочего стола постить в виде PNG по мегабайту каждый вместо более уместного тут восьмидесятипроцентного JPEG?

Офигеть. Какой мощный оказывается инструмент.

Перечисленное - плюсы. Это же получается можно свои мысли выражать как хочешь. Ну а то что идиоты их выражают по идиотски - язык не виноват.

Девятая "дичь" свойственна многим языкам. Для этого эпсилон есть и так не проверяют числа с плавающей точкой.

Если хочется точности, надо было брать decimal, а не float.

Decimal такой же неточный как и float только в других случаях, и он точно также является числом с плавающей точкой.
Console.WriteLine(1m/3m + 1m/3m + 1m/3m);
0.9999999999999999999999999999

https://dotnetfiddle.net/FU8NM4

Если ожидаете, что 1m/3m + 1m/3m + 1m/3m даст 1m ровно — то либо ещё не знакомы с конечной арифметикой, либо решили на хайпе словить баг там, где обычная математика.

а в чем принципиальная разница между 1/3 и 4/10 из примера в посте?

только в том что в 4/10 знаменатель 10, но 10 не какое то особенное число, не считая того что так получилось что на двух руках 10 пальцев. Если работать с данными из реального мира (не с финансами) то знаменателя 10 там не будет, и decimal будет иметь все недостатки float без достоинств.

Да тут даже языки не при чём, это связано с особенностями представления чисел с плавающей запятой в компьютере. И то, что C# использует виртуальную машину, сути не меняет.

Отнести это к разряду дичи может только человек, который не изучал самую базу и пришёл в программирование после курсов "войти в айти за 3 недели". Где о таких тонкостях просто некогда рассказывать.

Отнести это к разряду дичи может только человек, который не изучал самую базу и пришёл в программирование после курсов "войти в айти за 3 недели".

Ну ок, давайте немного расскажу об авторе:

I’m currently a game dev in Stockholm! I also have some compiler experience in the past: I’ve worked at Microsoft on the C# compiler, Mozilla on their JS engine, and Embark on rust-gpu. I graduated from Michigan Tech studying computer science.

У вас точно сопоставимый опыт имеется?

Система наследования общих классов в C# не настолько хорошо продумана как в Java, поэтому временами получаются нехорошие вещи:

Тем временем в языке с "настолько хорошо продуманной системой наследования":

interface ReadonlySome {
    int getN();
}
static class Some implements ReadonlySome {
    private int n;
    @Override
    public int getN() {
        return n;
    }
    public void setN(int n) {
        this.n = n;
    }
}
...
ReadonlySome roSome = new Some();
// вызовет ошибку
// roSome.setN(10);
((Some) roSome).setN(10);
// 10
System.out.println(roSome.getN());

Может быть перед тем как ерунду за другими повторять проверить, или немного подумать?

Не очень понял о чем вы.

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

Где оно тут сломано?
IReadOnlyList - интерфейс
List - класс
Где тут какой дочерний интерфейс. Вот такой же пример на Java. Там получается тоже сломано?

import java.util.ArrayList;
import java.lang.Iterable;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Creating and initializing an ArrayList
        ArrayList<Integer> originalList = new ArrayList<>(List.of(1, 2, 3, 4));
        Iterable<Integer> list = originalList;

        // Error
        // list.add(5);

        // However, we can cast back to ArrayList and modify the original list
        ((ArrayList<Integer>) list).add(5);

        // Output: 5
        System.out.println(originalList.size());
    }
}

List<Integer> list = new ArrayList<Integer>();

list.add(1);

final Iterator<Integer> it = list.iterator();

ааа понял вы специально так сделали, знание этих ошибок не улучшат ничего, так как ява предлагает удобный интерфейс для создания десктопных приложений)

Что вы хотели сказать этим куском кода?

Надо сначала тему изучить, а потом лезть. Интерфейс - это контракт, а не наследование. А то еще напиши 'ой смотрите, говорят множественного наследования нет, а вон какая дичь - несколько интерфейсов'

вон какая дичь - несколько интерфейсов'

Не подсказывайте, а то автор вторую часть напишет ;)

Не очень понял о чем вы.

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

Что в нем сломанного? У Вас есть интерфейс предоставляющий методы только для чтения. Эти методы - подмножество методов любой из реализаций. Вы берете и на свой страх и риск делаете жесткое приведение типов и получаете оригинальный тип с которым и работаете дальше. Что не так? IReadOnlyList это не Immutable тип, это просто представление подмножества методов только для чтения какой-либо из реализаций. То что Вы начинаете делаете жесткое приведение типов, забивая на замысел автора кода который дал Вам IReadOnlyList намекая что нужно пользоваться методами только для чтения, это уже Ваши проблемы а не проблемы языка. Можно с таких же успехом жаловаться на то что в языке сломана инкапсуляция потому что существует рефлексия.

И самое смешное в этом что Вы в качестве примера хорошо продуманного наследования приводите вообще Java, в которой это сделано настолько дико что даже говорить стыдно.

// Жил был Java список
public interface List<E> extends SequencedCollection<E> {
  E get(int index);
  E set(int index, E element);
  boolean add(E e);
  boolean remove(Object o);
  boolean addAll(Collection<? extends E> c);
  // etc
  // ...
}

Список в Java дает возможность как получать элементы так добавлять и удалять.

// Создадим список
List<Integer> list = List.of(1, 2, 3, 4, 5);
// Добавим в него 6
list.add(6);

Какие могут быть проблемы?

java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at com.gdiff.xpoligon.core.multithreading.VirtTest.name(Test.java:20)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Ой. Как же так? Интерфейс же позволяет модифицировать список.
Посмотрим метод of()

static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
    return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5);
}

Неловко вышло. Но видимо по Вашему так должна выглядеть "настолько хорошо продуманная система наследования". Когда нам приходит из метода List и мы понятия не имеем стрельнет оно или нет, хотя по контракту не имеет права.

А еще есть многие веселые моменты:

// Есть код
List<Integer> list1 = list.stream()
        .map(n -> n + 1)
        .collect(Collectors.toList());
// Но сонар и коллеги из JetBrains научили Idea подсказывать другой вариант
List<Integer> list2 = list.stream()
        .map(n -> n + 1)
        .toList();
// Пробуем
list1.add(6);
// 1,2,3,4,5,6
list2.add(6);

java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at com.gdiff.xpoligon.core.multithreading.VirtTest.name(Test.java:21)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

Здорово. Просто дизайн богов.

https://godbolt.org/z/ccW15eETE

метод с map функционал типо в туториалах пишут типо есть, но есть еще аналоги методы до него, можно и не пользоваться map оно всё равно будет работать

Справедливости ради, в .net все же присутствует похожая проблема.

Как-то не очень похожая. new[] {1,2,3} - это синтаксис для создания массива int[] и лист там не фигурирует. То есть тут код создает экземпляр класса Array и приводит его к интерфейсу IList. Array реализует IList только частично и это разумеется надо иметь в виду при приведении к интерфейсу. Тем более что анализаторы кода ругаются и рекомендуют использовать адекватный синтаксис вида:

IList<int> example = [1, 2, 3];

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


Класс может не поддерживать все методы интерфейса, это вполне распространенное явление. ReadOnlyCollection и Array реализуют IList и ICollection, но методы связанные с добавлением и удалением элементов у них выбрасывают NotSupported. Когда вы создаете и используете каку-то конкретную реализацию - выбор на вашей совести. Как и то, зачем вы вообще используете интерфейс в локальном методе, где прямым образом создаете конкретную реализацию. Нужен вам лист, ну так и объявляйте лист.

Это все не очень похоже на ситуацию, когда статический метод класса List, возвращает экземпляр класса-наследника, о котором он и знать-то по хорошему не должен, при этом возвращает как самого себя, то есть базовый класс, так что при вызове .of(..) у вас не существует никакого способа определить эту подставу, кроме как знать его реализацию. И подстава не была бы такой подставой, если бы наследник следовал принципу Барбары Лисков (вот уж не думал что буду упоминать его всуе), однако вот. Выходит, что вам нужен лист, вы честно создаете лист, собираетесь пользовать - да не тут-то было...

Оставлю для истории, поскольку в новых версиях дотнета уже не работает:

И никогда не работало ни в одной релизной версии. Приведена ссылка на нерелизную ветку рослина (main).

Не вводите людей в заблуждение, пожалуйста.

Посмотрите скриншот в оригинальном твите, там ветка main от декабря 2023го года. Насколько я понял этот баг (не фичу) закрыли в тот же год, но до этого оно вполне себе жило.

Это .NET 7 как минимум.

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

Написал же "для истории", чтоб боялись.

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

Я так понимаю, сделать:

dotnet new console -o Test
cd ./Test
dotnet run
Не позволила религия и пришлось писать сей огромный скрипт?

Это же сложно.. проще же скрипт написать на баше )

Я же написал что все примеры в статье это однострочники, генерировать проект ради однострочника это перебор.

Так что да, лучше один скрипт чем простыня генеренного кода на каждый такой однострочник.

"Сгенерировать проект" на C# - это буквально один файл .csproj из шести строк.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
  </PropertyGroup>
</Project>

К слову, можно использовать примерно вот такой проект что компиляции произвольно указанного файла:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
     <Compile Remove="@(Compile)" />
     <Compile Include="$(FileForCompile).cs" />
  </ItemGroup>
</Project>

Теперь новый челлендж попробуй 1С Предприятие)

Это запредельно жестоко ))

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

Автор поел говна и предлагает другим. Спасибо, но зачем?

Ничего такого страшного в примерах не увидел. Язык делает ровно то что у него попросил программист. В чём собственно говоря претензия?

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

в то время когда джава более открыта, дот нет на других платформах не такой как джава, одно это даёт удобство джаве, тоесть по дизайну юзабилити джава удобнее чем си шарп, если копнуть глубже такой гигант как Sun(UNIX) больше дал по технологиям и языку джава, чем решение сишарп от винды

как у сишарпа с posix простите, а полный отказ от корней посикс это я не знаю ) отказались от посикс и в чем выйгрыш?

не одним альтернативным Линуксом можно это закрыть, есть и должны быть другие альтернативы

мне интересно как шестую дич на джаве повторить )

Знание приносит страх. (ц) Futurama

Кто умножает познания, умножает скорбь (С) Царь Соломон

Это не C# отбитый, это авторы кода отбитые.
И отбинитые врули.

Дичь вторая: изменяемый «read only»
IReadOnlyList readonlyList = list;
// вызовет ошибку error CS1061
// readonlyList.Add(5);
У меня такое вообще не компилируется, потому что у интерфейса IReadOnlyList нет метода Add.

Дичь третья: изменяемые константы
var f = (in char x) => { /* нельзя изменить 'x' внутри этого блока */ };
f = (ref char x) => { x = 'A'; }; // но возможно тут!
Тоже не компилируется, нужен метод такой же сигнатуры.

Дичь четвертая: упоротый async await
С async async async(async async) => await async;
Тоже не компилируется вспомогательный код.
public async Task => null;
//CA1822: Method Task does not access instance data and must be masked as static
Хотя и хрен бы с ним, если автор запускает такое не в виндовском блокноте, то классы, методы и кейворды
должны выделяться разными цветами. Но автор опять лжет и выделяет все одним цветом.
Про появление async/await только в пятой версии языка и обратную совместимость я и говорить не хочу.

Дичь пятая: вызов без инициализации
Ну офигеть, автор сам использует какое-то изначально костыльное API, а потом сам на него жалуется!
В плюсах если что можно вообще любую область памяти привести к классу, и ничего - это проблема юзающего такое программиста.

Дичь седьмая: рефлексия без рефлексии
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct Union
{
[FieldOffset(0)] public Alpha AsAlpha;
[FieldOffset(0)] public Bravo AsBravo;
}
Ну и что вы этим хотели сделать? Что хотели - то и получили

Дичь восьмая: строка, которая хотела стать числом
И что, нормальное IDE различает class String и keyword string и пишет разыми цветами. У меня даже
отображается неявное приведение. А автора за такие имена - убить.

Дичь девятая: округление "по пацански"
Эта "дичь" есть везде, где есть арифметика чисел с плавающей запятой.
Плюс еще раз повторяю: нормальное IDE обругает за тачное сравнение.

А какая это KDE-тема на скриншотах?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории