.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1009. #ProjectManagement
3 Принципа Хорошей Разработки ПО. Окончание
Начало

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

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

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

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

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

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

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

Проблемы с продуктом имеют наибольшее значение после запуска. Проблема, которая возникает только в 0,1% случаев, может не быть замечена во время тестирования. Но как только у вас будет миллион пользователей, у вас будет нерешённая проблема с тысячей недовольных людей. Вам необходимо исправить проблемы, вызванные новыми мобильными устройствами, сбоями сети или атаками на систему безопасности, прежде чем они нанесут существенный вред вашим пользователям.

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

Источник: https://www.csc.gov.sg/articles/how-to-build-good-software
👍1
День 1010.
Ну что ж, время подводить итоги раздачи кружек. Всех желающих отобрал и записал в общий список в порядке появления комментариев. Для выбора победителей использовал простую случайную сортировку.

Результаты на картинке. Поздравляю победителей:
- Dmitriy Bobrovskiy (@RebelionTheGrey)
- Max Kaverin (@Kamaew)
- 𝕾𝖊𝖗𝖌𝖊𝖏 (@Xx_dexter_xX)
- Oleksandr Savchuk (@san_d0)
- Vasily Vasily (@Vasily26031988)
- Matviiv Andriy (@matviiv_a)
- Alexey Chernyaev (@FixGN)
- Georgy Levchenko (@FoxTes)
- Амир Хисматуллин (@ikeima)

Пожалуйста, отпишитесь мне в личку, куда прислать кружки.
День 1011. #Оффтоп
Давненько вам не рекомендовал видео. Поэтому сегодня в качестве компенсации нечто эпическое. Интервью Сергея Теплякова подкасту DotNet&More:
- Про собеседования
- Какие скилы сейчас важны?
- Работа в команде
- Будущее C#
- SOLID и прочие паттерны. Надо ли оно сейчас?
и про многое другое аж 2,5 часа.

Наслаждайтесь.

https://youtu.be/W7HqDCnQTkU
День 1012. #ЗаметкиНаПолях #AsyncTips
Обработка исключений из методов async Task

Задача
Спроектировать код для успешного результата несложно, но структура кода не может считаться правильной, если в ней не обрабатываются потенциальные ошибки. К счастью, обработка исключений из методов async Task реализуется прямолинейно.

Решение
Исключения можно перехватывать конструкцией try/catch, как для синхронного кода:
async Task ThrowExceptionAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
throw new InvalidOperationException("Test");
}

async Task TestAsync()
{
try {
await ThrowExceptionAsync();
}
catch (InvalidOperationException) {…}
}

Исключения, выданные из методов async Task, помещаются в возвращаемый объект Task. При использовании await с задачей, в которой произошёл отказ, первое исключение этой задачи выдаётся повторно. При этом исходная трассировка стека будет правильно сохранена.

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

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

Async Void
Что если имеется метод async void и требуется обработать исключения, распространённые из этого метода.

Хорошего решения не существует. Если возможно, измените метод так, чтобы он возвращал Task вместо void. Если вам требуется использовать метод async void, рассмотрите возможность упаковки всего кода метода в блок try/catch.

Существует и другой возможный способ. Когда метод async void распространяет исключение, это исключение выдаётся в контексте SynchronizationContext, активном на момент начала выполнения метода async void. Если среда выполнения предоставляет SynchronizationContext, то обычно она предоставляет механизм обработки этих высокоуровневых исключений на глобальном уровне. Например, WPF предоставляет Application.DispatcherUnhandledException, а ASP.NETUseExceptionHandler.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
День 1013. #ЧтоНовенького
Новые Способы Организовать Вкладки в VS 2022
В Visual Studio 2022 появилась возможность раскрашивать вкладки по проектам, чтобы было легче ориентироваться в открытых файлах. Для этого перейдите в Tools > Options > Tabs & Windows (Инструменты > Параметры > Вкладки и Окна) и выберите Colorize tabs (Раскрасить вкладки). Есть возможность раскрашивать по проектам и скоро появится возможность раскрашивать по типам файлов.

Вертикальные вкладки
Чтобы включить эту функцию, щёлкните правой кнопкой мыши любую вкладку и выберите Set Tab Layout > Place Tabs on the Left (Расположение Вкладок > Разместить Слева).

Гибкая настройка
Также добавили параметры, чтобы выделить активную вкладку жирным шрифтом, изменить максимальную/минимальную ширину вкладки и добавить дополнительную кнопку закрытия вверху документа. Это также можно сделать в Tools > Options > Tabs & Windows (Инструменты > Параметры > Вкладки и Окна).

Источник: https://devblogs.microsoft.com/visualstudio/personalize-docs/
День 1014. #ЧтоНовенького
.NET 6
Вчера состоялся релиз .NET 6. На канале уже было (и ещё будет) много постов про новинки. Здесь будет некоторое саммари. А в 19 по Москве стартует .NET Conf 2021, посвящённая .NET 6.

Основные нововведения в .NET 6:
- Эта версия .NET с долгосрочной поддержкой (LTS) – 3 года.
- Унифицированная платформа для браузера, облака, настольных компьютеров, Интернета вещей и мобильных приложений, использующих одни и те же библиотеки .NET и возможность легко обмениваться кодом.
- Производительность значительно улучшена по всем направлениям и, в частности, для файлового ввода-вывода.
- C# 10 предлагает улучшения языка, такие как записи-структуры, новые асинхронные методы, неявные директивы using, новые возможности LINQ и коллекций. В компилятор добавлены новые генераторы кода.
- Горячая перезагрузка позволяет пропустить пересборку и перезапуск приложения. Просмотр изменений возможен во время работы приложения. Поддерживается в Visual Studio 2022 и из .NET CLI для C# и Visual Basic.
- Облачная диагностика была улучшена с помощью OpenTelemetry и dotnet monitor, которые теперь поддерживаются в производственной среде и доступны в сервисе приложений Azure.
- API JSON более функциональны и имеют более высокую производительность с использованием генераторов кода для сериализатора.
- Минималистические API представлены в ASP.NET Core, чтобы упростить начало работы и повысить производительность сервисов HTTP.
- Компоненты Blazor можно динамически отрисовать из JavaScript и интегрировать в существующие приложения на основе JavaScript.
- Появилась AOT-компиляция WebAssembly для приложений Blazor WebAssembly (Wasm), а также поддержка повторного связывания времени выполнения и нативных зависимостей.
- Одностраничные приложения, созданные с помощью ASP.NET Core, теперь используют более гибкий шаблон, который можно использовать с Angular, React и другими популярными JavaScript фреймворками.
- Добавлена поддержка HTTP/3, чтобы ASP.NET Core, HttpClient и gRPC могли взаимодействовать с клиентами и серверами через HTTP/3.
- File IO теперь поддерживает символические ссылки и значительно улучшил производительность благодаря переписанному с нуля FileStream.
- Безопасность улучшена за счёт поддержки OpenSSL 3, схемы шифрования ChaCha20Poly1305 и средств глубокой защиты во время выполнения.
- Однофайловые приложения можно публиковать для Linux, macOS и Windows (ранее только для Linux).
- Тримминг IL теперь более функционален и эффективен с новыми предупреждениями и анализаторами для обеспечения правильных конечных результатов.
- Были добавлены генераторы и анализаторы кода, которые помогают создавать лучший, безопасный и производительный код.

Более подробно обо всех нововведениях и улучшениях см. в источнике ниже.

Источник: https://devblogs.microsoft.com/dotnet/announcing-net-6/
День 1015. #Карьера
Как Среднему Программисту Опережать Конкурентов
Код – это не вся моя жизнь. Я также не являюсь экспертом в структурах данных и алгоритмах. По сравнению с талантами в индустрии ПО я считаю себя средним программистом. Однако даже со средними навыками я никогда не прекращал расти. Быть средним программистом само по себе не мешает карьерному росту. Вы можете перерасти своих конкурентов, если будете готовы приложить усилия в правильном направлении.

1. Изучите домен компании
Хард скилы необходимы, чтобы быть разработчиком. Как средний программист, вы уже лучше многих. Всё, что нужно сделать, это добавить функциональные знания к своему инвентарю навыков. Интересуйтесь бизнес-целями каждого куска кода, который пишете. Знание примеров реального использования помогает писать наиболее простой код, отвечающий требованиям. Приложите максимум усилий для получения комплексных знаний о бизнес-логике.
Этого легко добиться, если вы работаете над приложением с первых дней. Если же вы присоединились к проекту по ходу, множество нюансов предметной области приходится изучать самостоятельно. База данных - лучшее место для начала (поскольку это наименее изменяющаяся часть). Следующим шагом будет понимание роли, которую данные играют в приложении и для компании. Вы будете поражены количеством бизнес-задач, которые можно эффективно решить, если вы знаете, что представляют собой функциональные сущности.

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

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

4. Предлагайте решения, а не просто озвучивайте проблемы
Ваше отношение к вызовам окажет гораздо более значительное влияние на ваш карьерный рост, чем навыки программирования. Люди ценят вовлечённость. Средний программист, преданный делу, может быть ценнее, чем отличный программист, который повсюду видит проблемы. Работая в команде, вы обязательно столкнётесь с проблемами: неэффективные рабочие процессы, коллеги, которые не выкладываются на полную, и т.п. Сосредоточьтесь на решении проблем, а не просто озвучивайте их. Однако имейте в виду, что, даже если есть разумные решения, не все проблемы можно решить в одночасье. Следовательно, разработчику нужно иметь много терпения, чтобы иметь дело с неэффективными людьми и процессами.

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

Источник: https://betterprogramming.pub/how-to-stay-ahead-of-the-competition-as-an-average-programmer-f76853677490
Автор оригинала: Lokajit Tikayatray
День 1016. #ЗаметкиНаПолях
Основы DDD с Помощью Пиццы
Предметно-ориентированное проектирование (Domain Driven Design) не предоставляет практических способов реализации архитектуры ПО, но фокусируется на нескольких основных принципах, которые могут помочь в создании поддерживаемого ПО. Чтобы понять эти концепции, мы возьмем реальный пример пиццерии. Для начала рассмотрим несколько терминов.

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

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

Модели
Строительные блоки домена. Различные части, которые объединяются для решения проблемы. В нашем случае это люди в их различных ролях, ингредиенты, пицца, мебель, машины и т.д.

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

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

Принципы DDD
1. ПО моделируется вокруг бизнес домена
Бизнес домен является основой всех архитектурных решений. Бизнес-модели и программные компоненты должны быть сопоставлены друг с другом. Независимо от того, используется ли термин разработчиком или руководителем компании, он должен означать одно и то же. Окончательная версия ПО - это отражение того, как работает бизнес.
Если владелец магазина использует термины маленькая, средняя и большая, рекомендуется, чтобы кассир использовал те же термины вместо размера пиццы в сантиметрах. Это упрощает понимание разговора для обеих сторон.

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

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

Преимущества DDD
1. Облегчает общение – все разговоры ведутся экспертами в предметной области.
2. Повышает гибкость – возможность каждой подсистеме развиваться независимо.
3. Снижает недопонимание за счет использования единых формулировок и терминов.
4. Улучшает координацию в команде из-за сужения областей контекстов.
5. Способствует появлению более чистой архитектуры, поскольку разделение задач снижает риск раздувания программных компонентов.

DDD не следует использовать, когда:
1. Не ожидается быстрого роста системы.
2. Первоначальная стоимость должна быть низкой.
3. Время на разработку ограничено.

Источник: https://dev.to/abh1navv/understanding-domain-driven-design-with-some-pizza-4gkn
День 1017. #ЧтоНовенького
Изменения в Структурах в C# 10
C# 10 представляет функции для структур, которые обеспечивают лучшее соответствие структур и классов.

Конструкторы без параметров и инициализаторы полей
До C# 10 каждая структура имела неявный общедоступный конструктор без параметров, который устанавливал для полей структуры значения по умолчанию. Создание конструктора без параметров для структуры было ошибкой.

Теперь вы можете добавлять конструкторы структур без параметров. Они должны быть публичными и не могут быть частичными. Если вы его не добавите, будет предоставлен неявный конструктор без параметров, устанавливающий для всех полей значения по умолчанию.
public struct Address {
public Address() {
City = "<unknown>";
}
public string City { get; init; }
}

Можно инициализировать поля в конструкторе без параметров, как выше, либо через инициализаторы полей или свойств:
public struct Address {
public string City { get; init; } = "<unknown>";
}

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

Структуры-записи
Начиная с C# 10, записи можно определять как структуры:
public record struct Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
Вы можете продолжить определять записи как классы либо просто по слову record, либо через record class для ясности.

Структуры и так сравниваются по значению. Структуры-записи добавляют поддержку IEquatable<T> и оператор ==, а также включают переопределение ToString().

Также поддерживается позиционная запись:
public record struct Person (string FirstName, string LastName);

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

Это упрощает преобразование кортежей в именованные типы. Изменение типов возвращаемых значений с кортежа, вроде (string FirstName, string LastName), на именованный тип Person, может очистить ваш код и гарантировать согласованные имена членов.

Чтобы создать неизменяемую структуру записи, добавьте readonly в определение структуры или отдельных свойств. Инициализаторы объектов являются частью этапа построения, на котором можно установить свойства только для чтения, поэтому следующая инициализация сработает и для readonly-структур:
var person = new Person { FirstName = "Mads", LastName = "Torgersen"};

Модификатор sealed для ToString() в классах-записях
Теперь метод ToString() может включать модификатор sealed, который не позволит его переопределять в производных типах записей.

Выражение with в структурах и анонимных типах
C# 10 поддерживает выражение with для всех структур, включая структуры-записи, а также для анонимных типов:
var person2 = person with {LastName = "Kristensen"};

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

Источник: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
День 1018. #ProjectManagement
Почему Надо Выбирать Простую Веб-архитектуру
Координация команды разработчиков — тяжелая работа! Когда команда предлагает вам варианты, не всегда легко понять, что выбрать и почему.

Почему стоит выбрать более простой вариант?
1. Экономия.
ПО требует денег на создание, улучшение и поддержку. Решения влияют на бюджет в долгосрочной перспективе. Чем сложнее веб-архитектура, тем больше нужно будет потратить в долгосрочной перспективе на запуск продукта, обеспечение безопасности и улучшения.
2. Обслуживаемость. У руководителей много различных забот и мало времени. Менее сложное ПО будет легче поддерживать в будущем, что сэкономит время и нервы.
3. Доступность. Более сложные архитектуры, такие как одностраничные приложения, могут быть менее доступны по умолчанию и требуют дополнительной работы для обеспечения доступности.
4. Производительность. Приложения, использующие сложные веб-архитектуры, зачастую дольше загружают даже относительно простые данные.

Более простой подход означает:
- Меньше уровней технологий
- Использование стабильных технологий вместо передовых
- По возможности меньше сложности

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

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

2. Если статического сайта недостаточно, сделайте серверное приложение
Здесь нужно подумать о создании приложения, базы данных и надёжном хранении пользовательской информации.
Когда это хороший выбор:
- нужна система регистрации и есть разные роли пользователей в приложении,
- нужна сложная логика или обработка данных.

3. Если требуется взаимодействие на стороне клиента, добавьте JavaScript
Веб-браузер может делать вычисления, анимацию и обрабатывать взаимодействия с пользователем. Интерактивность на стороне клиента означает отображение информации или обработку логики в самом браузере.
Когда это хороший выбор: «использование достаточного количества JS» можно рассматривать как способ добавить необходимую интерактивность, не добавляя ненужной сложности. Но нужно поставить вопрос, вводятся ли новые функции в ответ на требования пользователей и тестируются ли они на постоянной основе.

4. Если требуется сложная интерактивность на стороне клиента, может потребоваться одностраничное приложение (single-page application)
React, Angular, Vue.js или Blazor - все эти фреймворки используют JavaScript в браузере для предоставления пользователям функциональных возможностей. Для них часто требуются два набора приложений: на стороне клиента для интерактивности и на стороне сервера для доставки и хранения данных.
Когда это хороший выбор: одностраничные приложения предполагают компромиссы. Из-за нескольких уровней технологий их создание может быть более дорогостоящим и сложным в обслуживании. Часто требуется дополнительная работа, чтобы сделать приложение доступным. Безопасность может быть более сложной задачей. Одностраничные приложения могут быть правильным выбором, когда вы и ваша команда взвесили эти компромиссы и определили, что для предоставления основных функций приложения необходима интенсивная интерактивность на стороне клиента, автономность или отображение данных из публичного API.

Вот несколько вопросов, которые вы можете задать при выборе вариантов веб-архитектуры в проекте:
- Как эти варианты повлияют на доступность, пользовательский опыт и безопасность?
- Сколько разных языков программирования или наборов навыков понадобится, чтобы исправлять проблемы и развёртывать обновления?
- Есть ли пример успешного приложения на этой технологией в арсенале команды?
- Что будет, если команда сменится? Насколько легко или сложно будет нанять разработчиков, обладающих схожими навыками?

Источник: https://18f.gsa.gov/2021/04/05/why_simplicity_choosing_a_web_architecture/
День 1019. #ЧтоНовенького
Улучшения Интерполяции Строк в C# 10
Продолжаем знакомиться с новинками в новой версии C#.

Интерполированные обработчики строк
Как известно, компилятор превращает интерполированные строки в вызов string.Format. Это может привести к большому количеству аллокаций: упаковке аргументов, выделению массива аргументов и, конечно же, самой результирующей строки. Кроме того, он не оставляет места для манёвра в смысле самой интерполяции.

В C# 10 добавили библиотечный шаблон, который позволяет API «брать на себя» обработку аргумента в виде интерполированной строки. Когда используется интерполированная строка, компилятор проверяет, назначена ли интерполированная строка типу, который удовлетворяет шаблону обработчика интерполированной строки. Обработчик интерполированной строки - это особый тип, который преобразует интерполированную строку в обычную строку. Он применяется в специализированных сценариях обычно по соображениям производительности.

Для примера рассмотрим StringBuilder.Append:
var sb = new StringBuilder();
sb.Append($"Привет, {args[0]}, как дела?");

До сих пор это вызывало перегрузку Append(string? value), передавая туда аллоцированную и вычисленную строку, и добавляло её к StringBuilder одним блоком. Однако теперь есть новая перегрузка Append(ref StringBuilder.AppendInterpolatedStringHandler handler), которая имеет приоритет над предыдущей, когда в качестве аргумента используется интерполированная строка.

Авторы API проделали некоторую работу «под капотом» и добавили перегрузки с параметрами вида [Something]InterpolatedStringHandler, чтобы обрабатывать интерполированные строки более подходящим образом для своих целей. В нашем примере с добавлением строки " Привет, ", args[0] и ", как дела?" будут по отдельности добавлены к StringBuilder, что намного эффективнее, хотя имеет тот же результат.

Иногда вы хотите выполнить работу по построению строки только при определенных условиях. Примером является Debug.Assert:
Debug.Assert(condition, $"{КакойТоСложныйМетод()}");

В большинстве случаев condition будет true, и второй параметр не будет использоваться. Однако все аргументы вычисляются при каждом вызове, излишне замедляя выполнение. Debug.Assert теперь имеет перегрузку с обработчиком интерполированных строк, который гарантирует, что второй аргумент даже не вычисляется, если условие истинно.

Наконец, вот пример фактического изменения поведения строковой интерполяции. String.Create() позволяет вам указать IFormatProvider, используемый для форматирования заменяемых выражений:
String.Create(CultureInfo.InvariantCulture, $"Ответ: {result}");
Значение result (число или дата) будет отформатировано в соответствии с переданной культурой.

Константные интерполированные строки
Если все замены в интерполированной строке являются константными строками, тогда результирующая строка также будет константой. Это позволяет использовать синтаксис интерполяции строк в большем количестве мест, например в атрибутах:
[Obsolete($"Используйте {nameof(OtherMethod)}")]

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

Источник: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
День 1020. #ЗаметкиНаПолях #AsyncTips
Когда Использовать ValueTask. Начало
ValueTask<T> используется как возвращаемый тип в ситуациях, в которых чаще всего может быть возвращён синхронный результат, а асинхронное поведение встречается реже. Обычно следует использовать Task<T>, а не ValueTask<T>. Рассматривать использование ValueTask<T> в качестве возвращаемого типа следует только после профилирования, которое показывает, что это приведёт к повышению быстродействия.

Простейший способ реализации метода, возвращающего ValueTask<T>, основан на использовании async и await, как и обычный async-метод. Нередко метод, возвращающий ValueTask<T>, способен немедленно вернуть значение. В таких случаях можно применить оптимизацию для этого сценария с использованием конструктора ValueTask<T>, и передавать управление медленному асинхронному методу только при необходимости:
public ValueTask<int> MethodAsync()
{
if (CanBehaveSynchronously)
return new ValueTask<int>(13);

return new ValueTask<int>(SlowMethodAsync());
}
private Task<int> SlowMethodAsync();

Возможны ситуации, в которых требуется реализовать метод, возвращающий ValueTask<T>. Например, при использовании интерфейса IAsyncDisposable, метод DisposeAsync которого возвращает ValueTask. В C# 8.0 и .NET Core 3.0 появилось асинхронное освобождение ресурсов. В BCL появился новый интерфейс IAsyncDisposable, который является асинхронным аналогом IDisposable. Также была введена команда await using — асинхронный аналог using. Таким образом, типы, которые собирались выполнить асинхронную работу при освобождении, получили такую возможность. Возвращаемым типом DisposeAsync является ValueTask, а не Task.

В следующем примере показана реализация IAsyncDisposable, которая выполняет свою логику асинхронного освобождения однократно. При следующих вызовах метод DisposeAsync завершается успешно и синхронно:
class MyClass : IAsyncDisposable
{
private Func<Task> _disposeLogic;

public ValueTask DisposeAsync()
{
if (_disposeLogic == null)
return default;

// Этот простой пример непотокобезопасный;
// если сразу несколько потоков вызовут DisposeAsync,
// логика может быть выполнена более одного раза.
Func<Task> logic = _disposeLogic;
_disposeLogic = null;

return new ValueTask(logic());
}
}

Использование:
await using (var myClass = new MyClass())
{
...
} // Здесь вызывается DisposeAsync (с ожиданием)

Большинство методов должно возвращать Task<T>, поскольку при потреблении Task<T> возникает меньше скрытых ловушек, чем при потреблении ValueTask<T>. Чаще всего при реализации интерфейсов, использующих ValueTask или ValueTask<T>, можно просто применять async и await.

Окончание следует…

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Главы 2, 11.
День 1021. #ЗаметкиНаПолях #AsyncTips
Потребление ValueTask
Когда Использовать ValueTask

Задача
Требуется организовать потребление ValueTask<T>.

Решение
В большинстве случаев все, что необходимо сделать – добавить await:
ValueTask<int> MethodAsync();

int value = await MethodAsync();

Также можно выполнить await после выполнения конкурентной операции, как в случае с Task<T>:
ValueTask<int> valueTask = MethodAsync();
// Другая параллельная работа.
int value = await valueTask;

Однако следует помнить, что ValueTask или ValueTask<T> может ожидаться только один раз.

Чтобы сделать что-то более сложное, преобразуйте ValueTask<T> в Task<T> вызовом AsTask:
Task<int> task = MethodAsync().AsTask();
// Другая параллельная работа.
int value = await task;
int anotherValue = await task;
Многократное ожидание Task<T> абсолютно безопасно.

Также возможны другие операции, например, асинхронное ожидание завершения нескольких операций:
Task<int> task1 = MethodAsync().AsTask();
Task<int> task2 = MethodAsync().AsTask();

int[] results = await Task.WhenAll(task1, task2);

Тем не менее для каждого ValueTask<T> можно вызвать AsTask только один раз. Также вы не можете одновременно использовать await и вызвать AsTask для одного ValueTask<T>.

Другие свойства ValueTask<T> предназначены для нетривиального использования. Обычно они работают не так, как другие известные свойства. В частности, для ValueTask<T>.Result действуют более жёсткие ограничения, чем для Task<T>.Result. Код, который синхронно получает результат от ValueTask<T>, может вызвать ValueTask<T>.Result или ValueTask<T>. GetAwaiter().GetResult(), но эти компоненты не должны вызываться до завершения ValueTask<T>. Синхронная загрузка результата из Task<T> блокирует вызывающий поток до завершения задачи; ValueTask<T> таких гарантий не даёт. Синхронное получение результатов от ValueTask или ValueTask<T> может быть выполнено только один раз, после завершения ValueTask, и это значение ValueTask уже не может использоваться для ожидания или преобразования в задачу. В противном случае вы получите непредсказуемый результат операции.

Итого
Когда ваш код вызывает метод, возвращающий ValueTask или ValueTask<T>, он должен либо немедленно выполнить await для этого ValueTask, либо немедленно вызвать AsTask для преобразования в Task. Возможно, эта простая рекомендация не исчерпывает все нетривиальные сценарии, но большинству приложений этого будет вполне достаточно.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
👍3
День 1022. #ЧтоНовенького
Улучшения Лямбда-Выражений в C# 10
Последний (по крайней мере пока) пост о новинках в новой версии C#. На всякий случай вот предыдущие посты о C# 10 и .NET 6:
- Улучшения в Асинхронности
- Улучшения в LINQ 1, 2
- PriorityQueue
- Статические абстрактные члены интерфейсов
- Обобщённая математика
- IEnumerable.Chunk()
- Глобальные Директивы Using
- Неявные Директивы Using
- Изменения в Структурах
- Улучшения Интерполяции Строк

Выведение типа в лябмдах
Лямбда-выражения теперь могут иметь «естественный» тип, то есть компилятор часто может самостоятельно определить тип лямбда-выражения. До сих пор лямбда-выражение приходилось преобразовывать в тип делегата или выражения. В большинстве случаев вы использовали один из перегруженных типов делегатов Func<…> или Action<…>:
Func<string, int> parse = (string s) => int.Parse(s);

Начиная с C# 10 компилятор может вывести тип за вас. Вы можете навести курсор на переменную и убедиться, что её тип всё так же Func<string, int>:
var parse = (string s) => int.Parse(s);

Обычно компилятор будет использовать доступный делегат Func или Action, если подходящий существует. В противном случае он будет синтезировать тип делегата (например, если у вас есть ref-параметры или большое количество параметров). Не все лямбды имеют естественные типы - некоторым просто не хватает информации о типах. Например, если не указывать типы параметров, компилятор не сможет решить, какой тип делегата использовать, что приведёт к ошибке компиляции:
var parse = s => int.Parse(s);

Вывод естественного типа лямбды означает, что она может быть присвоена более базовому типу, например, object или Delegate:
Delegate parse = (string s) => int.Parse(s);

В деревьях выражений используется комбинация «целевой» и «естественной» типизации. Если целевым типом является LambdaExpression или необобщённый Expression (базовый тип для всех деревьев выражений), а лямбда имеет естественный тип делегата D, будет выведен тип Expression<D>:
Expression parseExpr = (string s) => int.Parse(s);
Здесь для parseExpr будет выведен тип Expression<Func<string, int>>.

Типы возврата для лямбда-выражений
В предыдущих примерах тип возвращаемого значения лямбда-выражения был очевиден и выводился. Это не всегда так:
var choose = (bool b) => b ? 1 : "two";
// Ошибка, т.к. тип результата непонятен

В C# 10 вы можете указать явный тип возвращаемого значения для лямбда-выражения прямо перед параметрами. При этом параметры должны быть заключены в круглые скобки:
var choose = object (bool b) => b ? 1 : "two";
// Func<bool, object>

Атрибуты лямбда-выражений
Теперь вы можете помещать атрибуты в лямбда-выражения так же, как для методов и локальных функций. Cписок параметров при этом также должен быть заключен в круглые скобки:
Func<string, int> parse = 
[Example(1)] (s) => int.Parse(s);

var choose =
[Example(2)][Example(3)] object (bool b)
=> b ? 1 : "two";

Как и для локальных функции, к лямбдам могут применяться только атрибуты, имеющие AttributeTargets.Method.

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

Источник: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
Какую версию языка C# вы используете в вашей основной деятельности?
Anonymous Poll
13%
Уже 10 и .NET 6
41%
9 и .NET 5
22%
8 и .NET Core 3.x
3%
7 и .NET Core 2.x
16%
7 в .NET Framework
6%
Более старую версию (другую платформу)
День 1023. #Testing
Когда Использовать Mock-объекты
В тестировании можно пускаться в разные крайности относительно создания объектов-имитаций (mock-объектов). От имитации всего на свете до полного отказа их использовать. Обычно имитировать стоит только один конкретный тип зависимостей - неуправляемые зависимости: зависимости вне процесса, взаимодействия с которыми наблюдаются извне (SMTP-сервис, шина сообщений и т.д.).

Обычно существует несколько классов на пути от пользовательского ввода до вызова неуправляемой зависимости. Например, когда UserController регистрирует пользователя, ему может потребоваться отправить email с подтверждением регистрации. Для этого контроллер может использовать вспомогательный класс, который сам использует другой класс и так далее, в конце концов вызывая SMTP-сервис.

Вам нужно имитировать акт отправки email (потому что SMTP-сервис является неуправляемой зависимостью), но какой именно класс имитировать?

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

Ещё один совет - имитировать только те типы, которыми вы владеете. Впервые он был представлен Стивом Фриманом и Натом Прайсом в их книге «Growing Object-Oriented Software, Guided by Tests».

Суть в написании собственных адаптеров поверх сторонних библиотек и имитации этих адаптеров вместо библиотечных типов. Это полезно, потому что:
1. Часто у вас нет глубокого понимания того, как работает сторонний код.
2. Даже если этот код уже предоставляет удобный интерфейс, имитировать этот интерфейс рискованно, потому что вы должны быть уверены, что поведение, которое вы имитируете, соответствует тому, что на самом деле делает внешняя библиотека.
3. Адаптеры абстрагируют несущественные технические детали стороннего кода и определяют отношения с библиотекой в условиях вашего приложения.
4. Если библиотека изменится, не придётся обновлять все тесты (и производственный код), которые с ней работают. Оболочка помогает ограничить такие изменения одним местом - самой оболочкой.

Обратите внимание, что правило имитации только тех типов, которыми вы владеете, не является на 100% строгим. Главное ограничение - ваше понимание того, как работает внешняя библиотека. Если эта библиотека тривиальна (до такой степени, что её поведение становится очевидным), то прямая имитация библиотеки становится приемлемой.

Гойко Аджич уточнил правило «Имитируйте только те типы, которые принадлежат вам», на «Имитируйте только те типы, которые вы понимаете».

Источник: https://enterprisecraftsmanship.com
Автор оригинала: Vladimir Khorikov
День 1024. #ЧтоНовенького
Однофайловые Приложения в .NET 6
В .NET Core 3.0 мы познакомились с концепцией публикации приложения в виде одного exe-файла. Но в этом релизе было несколько вещей, которые не понравились людям. Основными проблемами были:
- Единственный файл exe на самом деле был самораспаковывающимся zip-архивом, который распаковывался во временное место и затем запускался. Иногда это создавало проблемы с точки зрения безопасности.
- Размер файла был астрономическим (70Мб для «Hello World»), хотя надо сказать, что он включает в себя всю среду исполнения .NET Core, поэтому на целевой машине не нужно было ничего устанавливать.

Давайте посмотрим, что изменилось в .NET 6.

Создадим простейшее консольное приложение:
Console.WriteLine("Hello, World!");
Console.ReadLine();

Чтобы опубликовать это как единый исполняемый файл, выполним команду в терминале:
dotnet publish -p:PublishSingleFile=true -r win-x64 -c Release --self-contained true

Заметьте, что когда вы публикуете один файл, вы должны включать целевой тип ОС, так как exe поставляется специально для этой ОС.

Если открыть папку MyProject\bin\Release\net6.0\win-x64\publish, там будет один EXE-файл. Он довольно большой, ~60Мб, (хотя немного меньше, чем был раньше). Исполняемый файл с параметром --self-contained false получается размером в 151Кб. Понятно, что размер – цена включения среды исполнения.

Самораспаковывающегося архива, больше нет
В .NET 6 практика упаковки содержимого в большой архив была изменена на один настоящий исполняемый файл, который загружается в память, а не извлекается во временные папки. Хотя, сжатие можно по-прежнему включить с помощью флага:
-p:EnableCompressionInSingleFile=true

Тримминг IL
.NET 6 имеет возможность убирать ненужные зависимости из вашего приложения. По умолчанию, когда вы публикуете автономное приложение, вы получаете всё. Но, используя функцию тримминга, вы можете удалить из среды исполнения зависимости, которые вы на самом деле не используете.

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

Попробуем:
dotnet publish -p:PublishSingleFile=true -r win-x64 -c Release --self-contained true -p:PublishTrimmed=true

У меня получился файл размером ~10Мб. Ещё раз замечу, что это всё приложение, включая среду исполнения. Неплохо!

Источник: https://dotnetcoretutorials.com/2021/11/10/single-file-apps-in-net-6/
День 1025. #юмор
День 1026. #ВопросыНаСобеседовании
Сегодня порекомендую вам очередное видео от Сергея Немчинского, в котором он отвечает на популярные вопросы про собеседования.

В какой одежде приходить? Что, если вы опоздали? Сколько времени длится собеседование? Нужно ли писать код на листочке? Почему спрашивают то, что не пригодится на конкретной позиции? Почему компании не дают фидбек в случае, если вы не подошли? И про многое другое.

Честно говоря, после просмотра очень хотелось прособеседоваться у Сергея. Жаль, что он не специализируется в .NET.

https://youtu.be/YeKmEmM7I2A
День 1027. #Карьера
Софт-Скилы, Которые Вам Потребуются, Чтобы Добиться Успеха
При собеседовании на джуна в основном оценивают ваши навыки программирования и ничего больше. При собеседовании на более старшие вакансии эти навыки также важны, но требуется и кое-что ещё. Вот пять ключевых «софт»-навыков, которые действительно выделяют хорошего разработчика ПО из толпы.

1. Эффективная коммуникация
Умение хорошо объяснять ваши идеи другим.
Создание ПО - командный вид спорта. Команда состоит из людей с разным опытом, убеждениями, предубеждениями и знаниями. Если вы хотите создать хороший продукт, вы должны хорошо работать вместе. Система, которую вы разрабатываете, будет имитировать структуру коммуникации вашей организации: плохое общение между членами команды приведет к плохо спроектированным продуктам.
Лучшие разработчики грамотно доносят сложные технические концепции нетехническим людям или младшим специалистам. Вы далеко пойдёте как разработчик, если сможете эффективно общаться и учить других.

2. Эмпатия
Умение ставить себя на место пользователей.
Разработчик должен руководствоваться предназначением своего продукта. Конечно, интересно изучать новые технологии или копаться в последних инструментах разработки, но как насчёт того, почему именно важна наша работа?
Лучшие разработчики заботятся о цели, ради которой они создают ПО, и стремятся понять людей, которым помогают. Существует распространённое упражнение по управлению продуктом под названием «Сопоставление интересов», которое направлено на чёткое определение того, как пользователь думает, чувствует и взаимодействует с продуктом. Понимая поведение и чувства пользователей, мы можем создать продукт, который они будут действительно использовать по назначению.
Слишком часто продукты создаются без предварительного разговора с пользователями. Даже если вы разработчик в команде, понимание того, как думает ваш пользователь, приведёт к улучшению качества продукта.

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

4. Ответственность
Уверенность ваших коллег в том, что вы справитесь со своей работой.
В команде люди полагаются на то, что вы сделаете свою работу, особенно если вы пообещали выполнить задачу. Если вы надёжны, никому не нужно будет следить за вашими успехами, поскольку вы доказали, что можете брать на себя ответственность.
Руководителям нужны разработчики, которым не нужна няня. Им нужны подчиненные, которые соглашаются что-то делать, и выполняют то, за что берутся. Вы не поверите, но многие люди ненадёжны, поэтому ответственный разработчик никогда не пропадёт.

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

Источник: https://betterprogramming.pub/5-soft-skills-you-need-to-succeed-as-a-developer-357f7eac3372
Автор оригинала: Marisa Hoenig
👍1