Обновить
4

Пользователь

Отправить сообщение
Будет еще одна часть? Вряд ли это все библиотеки. Например есть еще clize.
Есть еще SpanJson. Тот еще велосипед, судя по оформлению истории комитов, но зато поддерживает аттрибуты-конвертеры.

Набросал бенч на коленке сериализацию и десериализацию и получилось:

core 2.2.2

Newtonsoft: 1 (allocated 35.48 MB)
Velo: 0.43 (allocation 12.36 MB)
SpanJson: 0.18 (allocation 2.75 MB)

Newtonsoft: 1 (allocated 25.45 MB)
Velo: 0.36 (allocation 5.93 MB)
SpanJson: 0.27 (allocation 4.4 MB)
Разве?.. По логике вещей идет обычное планирование продолжения в ThreadPool и сначала будут сделаны запланированные ранее задачи, чем дело дойдет до этого продолжения. Иначе не будет разницы между обычными потоками без await. Нет никакой гарантии выполнения в том же потоке при ConfigureAwait(false), да и ConfigureAwait(true) тоже, если нет контекста, который жестко прибьет гвоздями продолжение к какому-то потоку.

Может тяготеть исполнять в том же потоке — да, но это не гарантируется.
А с чего он взялся решать такие вопросы
Именно так. Конечно можно сказать, что не дело сериализатора проверять заполненность и пусть проверяет уже на месте. Но если апи четко объявляет, что такого не будет (спека), то нарушение постусловия означает, что система работает неправильно.
Допустимо ли это и пропустить дальше или упасть «fail fast» зависит от приложения. Все эти «fail fast» обычно все исчезают в процессе разработки и тестирования. И если далее такое будет, то это ну совсем нештатная ситуация, которая требует немедленного внимания, потому что логике быть не должна ни в коем случае.

И да, поле с возможностью null будет nullable (или помечено аттрибутом, раз уж #nullable не зашел)

Постусловия можно поменять
Нельзя. Если имеется код, который завязан на контракт (спеку) кода сериализации, то ничего менять уже нельзя.

зы: конечно вы правы про то, что «каждый должен заниматься своим делом». Но подход fail-fast себя оправдывает, если ошибки, которые он ловит, можно починить до релиза. Если же это сервис, который должен быть устойчивый ко всем «а что если», то там предусматриваются все варианты, а не «зови программиста»
Ну это мы в сторону ушли (моя вина). Не должен транспортный давать гарантии модели. Он должен давать гарантии получения данных «не потерялось и дошло как надо». Но мы ушли в обсуждение архитектуры, а ведь проблема куда проще.

Речь идет о сериализаторе. К нему есть требование дать гарантию, что он заполнит все поля в классе, тип которого ему передадут для материализации (и они не будут дефолтными). Это у него постусловия такие. И сериализатор достаточно глуп, чтобы отследить это прямо в процессе разбора данных. Приходится проверять постусловием
Эти постусловия четко отражены в спеке и следуя «концепции баррикады» можно не писать дополнительные проверки на это в клиентском коде и не получить NRE.

и с #nullable в их текущей реализации выбор очевиден
1 инициализировать дефолтные и поломать постусловия, от чего пострадает клиентский код (breaking change) — и все это с нулевой выгодой
2 заставить в клиентском коде проставить везде "!" и заставлять в новом коде это писать — и снова выгоды не вижу
3 оставить все как есть.

Все зависит от точки зрения. Ваша цепочка выглядит примерно так:

--> json --> десериализация в PersonDTO --> Person(PersonDTO dto) -->

«десериализация в PersonDTO» может быть библиотекой-клиентом к некому апи, которую клиентский код может использовать. Она, можно сказать, транспортный слой, который при этом ничего не знает про business logic. И нормально, что этот слой валидирует полное получение данных, хоть и не может валидировать их суть. Я получаю ошибку сразу «fail fast», и к тому же на уровне, где она произошла.

Если я получаю из api погоду в виде иммутабельного объекта Weather c 3мя свойствами «город, температура, влажность», то рассматривать Weather как dto и создать WeatherModel, или просто использовать как есть? — каждый решает сам, но в случае «использовать как есть» у нас все равно есть гарантия транспортного слоя, что «город не null» и «температура настоящая, а не дефолтная»

И если переделка под nullable привносит только необходимость прописывать восклицательные знаки (или же отказ от проверок транспортного слоя), то я лучше откажусь от nullable
Возможно и не успел. Поэтому и спросил. На гитхабе сложно следить где что там решили, да и часто это просто болтовня, а не «решили».

Пример реальной попытки: сериализатор требует пустого конструктора и заполняет только те свойства, что может заполнить (причем через internal сеттеры). Если не заполнены, то это нештатная ситуация и простой валидатор проверяет на null зная, что ни одно поле не может быть null. Сложный валидатор невозможен — ему тут не место. И все работает, все хорошо, только фатальный недостаток — не использует новых фич

Пробую #nullable. Инициализация дефолтными значениями не прошла. Сразу поломался валидатор на незаполненность, а больше ничего и не проверить. Попытка обвесить модельки #nullable disable тоже ничего не дало. В итоге сделал свойства моделек nullable и начал массово (реально в сотнях мест) проставлять! но на полпути задался вопросом «а зачем?»

Код остался прежним, но я получил требование в клинтском коде писать! (и необходимость помнить, что! обязателен, ведь non-null гарантирован логикой, nullable из-за компилятора), а вот выгоду я увидеть не смог

и придумать как переделать, да чтобы не городить жуткие костыли из-за слабости поддержки nullable в языке, я тоже не смог, поэтому вернул все обратно.
Если по дефолту сделать nullable
Ну так оно и так во всем существующем коде по дефолту nullable. И все эти годы так было. И именно это «по дефолту null и инициализации не было и никто не подсказал про забытую инцицализацию» и является основной причиной ошибок NRE.

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

А так лучше бы они records сделали — крайне полезная вещь.
Вы не понимаете. Хватает ситуаций «я уверен, что на момент чтения оно не будет null, поэтому не заставляй меня делать его nullable, раз уж у компилятора не хватает ума самому это вычислить». В котлин для этого есть модификатор lateinit (по названию видно что он делает). Это и различные dto без конструкторов, методы вида Init, на границе с UI тоже пригождается и на границе с жавой (там ведь тоже никакого null safety). Вот прям такая же ситуация и с шарпом — множество обычного кода, который знать не знает про nullable

Инициализация в конструкторе это костыль
1. Я уже говорил, что эта инициализация просто нагружает GC лишний раз. Можно сказать что это короткоживущие объекты, но есть пункт 2
2. Может быть совсем не просто инициализировать свойство, которое суть сложный объект, да еще с приватным/интернал конструктором (сериализатору это без разницы, а вот вы будете в тупике)
3. И даже если можно, то в случае проблем с десериализацией вы можете получить неконсистентное состояние (новые данные + бесполезное дефолтные)

А теперь про гарантии. Вдруг десериализатор не заполнит некоторые свойства. Сравните:
а) В случае lateinit я просто сделаю проверку на null всех свойств 1 раз (и все) после десериализации. Я выполню гарантии в рантайме и у меня есть способ сказать компилятору «я сам».
б) в случае «дефолтные в конструкторе» я не смогу отличить что новое, а что старое. Мне придется делать полный валидатор, что уже перебор. Также он может быть невозможен, если значение свойства не отличить — и тогда придется все делать nullable. А это приведет к тому, что в 1000 мест мне придется проставлять!

Ожидалось, что шарп не будет изобретать свое, а возьмет удачные решения с других языков, но он взял только базовое и это явно недостаточно.
А в чем отличие? Ну кроме места где будет инициализация? Результат тот же.
Nullable Reference Types — то, что должно быть изначально. В котлине он изначально и там с этим очень неплохо. А вот в c#… прямо сейчас я чиню очередной NRE, причем в библиотеке, а ведь его могло бы и не быть, будь от этого защита.

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

Просто «поздно решили внедрить» и поэтому приходится делать это костыльно
Другое. Запрос к api возвращает json, который десериализуем в граф объектов. И хоть мы знаем, что у нас после этого будут инициализированы все свойства, приходится определять все поля nullable и потом в тысяче мест добавлять!.. Или же инициализировать свойства значениями, что в случае вложенных классов может быть очень развесисто и попросту захламляют кучу, ведь сразу после создания объекта все эти свойства будут перезаписаны.

А хотелось бы сказать компилятору «да я уверен, что там null не будет, не заставляй меня их инициализировать»
А будет ли в c# аналог lateinit того же Котлина?
И снова не вижу разницы.

Folder «C:\Windows\WinSxS»

Folders: 23887
Files: 81983
Files size: 6,33 GB
Allocated size: 6,31 GB (99%)

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

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

Готовых утилит нет, ибо никому не нужно.

Ну почему же. Нужно и есть. Вот та же du

Но я неправильно смотрел. Ключ -u не исключает хардлинки, а, наоборот, включает хардлинки в сумму (вот что значит прочесть so вместо справки)

без него у WinSxS нет особой разницы, а windows 21 гб.
И где и как это смотреть?
проводник c:/Windows/WinSxS:
размер: 6 793 220 351
на диске: 6 951 350 272

> du -u c:/Windows/WinSxS (du от sysinternals)
Files: 81979
Directories: 23888
Size: 6 793 154 099 bytes
Size on disk: 7 123 623 936 bytes
ладно. может хардлинки не внутри WinSxS, а вообще вся папка винды
проверяем

проводник c:/Windows/:
размер: 27 590 010 367
на диске: 26 928 803 840

> du -u c:/Windows/
Files: 151579
Directories: 32268
Size: 27 351 525 630 bytes
Size on disk: 26 939 879 424 bytes

неправильно смотрю? чем еще посмотреть?
Как раз при проблемах с Windows Update и предлагают чистить этот кэш.

Потому что при проблемах с инсталлом/апдейтом чего угодно это самый частый рецепт. А в случае с виндой, так вообще первый с ее невразумительными ошибками и бесполезной службой «обнаружение проблем».
Только вот чистка этого кеша при нормальной работе службы как раз и может привести к тем проблемам обновления. У меня приводила, хоть и чистил правильно останавливая службы. И хоть я использую уже wumt, но трогать эту папку = риск

отлично справится и оригинальный его дистрибутив.

Ну это как сказать. Недавно вычистил package cache (не nuget) от VS2017 — так даже родной инсталлер не помог. Это хорошо, что есть в поставке студии «InstallCleanup.exe -full», но полная переустановка всей студии и всех расширений мало приятное занятие.

Но все же размер этой папки просто ужасен. Решил рискнуть и нагуглил PatchCleaner, который вычистил из Installer 7.6 гига хлама по его мнению.
Очистка SoftwareDistribution с большой вероятностью сломает windows update. Сколько его раньше чистил — столько и ломалось.
А чистка Installer приведет к проблемам с установленным софтом.
Очистку этих папок можно себе позволить только если больше не хочется обновлять windows или менять установленный софт
C:\Windows — 35 GB
из них
C:\Installer — 12 GB
C:\SoftwareDistribution — 3 GB
То есть 15 гигов лежат файлы, которые всегда можно скачать. И их тронуть нельзя.
C:\WinSxS — 6.3 GB
Бесполезный неудаляемый жир, который винда накопила и похудеть ее не заставишь.
C zaborona проще. Для тех, кто не дружит с консолью, есть пакет luci-app-wireguard, а также есть скрипты настройки сервера. Да и правила меняются редко, что убирает необходимость автоматического обновления.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность