.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День семьсот шестой. #юмор
День семьсот седьмой. #ЧтоНовенького
Mapster
У Nick Chapsas вышло интересное видео про маппер под названием Mapster. "Как AutoMapper, только намного, намного быстрее", - как утверждают создатели. Но для начала приведу парочку тезисов Ника, которые мне показались интересными.

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

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

Но упомянуть это видео я решил ещё по одной причине. Я как-то уже писал про генераторы кода, новую функцию, появившуюся в среде .NET. Так вот, Mapster позволяет сгенерировать для класса домена код как маппера, так и класса DTO, просто добавив атрибут к классу домена. Довольно круто. При этом бенчмарк сгенерированного маппинга по времени такой же быстрый (а то и превосходит) ручной маппинг, и примерно в 4 раза быстрее, чем AutoMapper. То есть даже о тех микроскопических потерях в скорости при использовании автоматического маппинга относительно ручного, теперь говорить не приходится.

Подробности бенчмарка и описание работы Mapster'а в видео по ссылке ниже.

Источник: https://www.youtube.com/watch?v=UIslFVEHkzA
👍1
День семьсот восьмой.
Функциональное программирование. Начало
Термин функциональное программирование может сбивать с толку. Во многих ООП языках есть функции, замаскированные под методы объектов. Что значит этот новый термин?

Во-первых, термин не новый. Концептуально функциональное программирование как лямбда-исчисление было разработано еще в 1930 году Алонзо Черчем, а первый функциональный язык программирования LISP был доступен к концу 50-х годов.
Во-вторых, слово функция здесь не обозначает методы, которые мы называем функциями в программировании, а скорее относится к математическим функциям, таким как синус и косинус. И именно их чистота отличает их от функций в императивных языках программирования. Чистая функция при одном и том же вводе даёт один и тот же результат, без побочных эффектов (то есть, не изменяя состояния никаких других объектов). В отличие от функционального, императивное программирование не заставляет вас делать функции чистыми.

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

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

Тогда зачем его использовать?
ФП бережёт разум и нервы разработчика! Рассмотрим на примере. Предположим, у нас есть класс Car со свойством Color и двумя методами Run() и Stop(). Метод Run делает все, что необходимо для запуска машины. Рассмотрим следующий код:
var car = new Car();
car.Run();
… //какой-то код
SomeOtherFunction(car);
… // ещё код
SomeOtherFunction остановила машину? Непонятно, надо заглянуть в реализацию:
SomeOtherFunction(Car car) {
//машина уже запущена? Непонятно
car.Run();
//возможно, мы запустили уже запущенную машину
//что произойдёт в этом случае?
}

Здесь у нас 3 проблемы:
1. Как автор SomeOtherFunction, как я узнаю, был ли ранее вызван метод Run? Мне нужно найти вызывающий код, т.е. для разработчика это дополнительная нагрузка. Можно добавить свойство IsRunning, но ничто не заставляет меня проверять его значение. И не всегда такие свойства и методы связаны между собой. Всё равно нужно проверять вызывающий код. Хуже того, если SomeOtherFunction публичная в моём API, мне придётся прибегать к защитному программированию, документировать свой API, чётко описывать ожидаемое состояние входных данных и молиться, чтобы пользователь моего API прочитал документацию.

2. Что произойдёт, если я дважды вызову Run? Исключение? Второй вызов проигнорируется? Единственный способ узнать это - прочитать исходный код класса Car. Ещё одна головная боль.

3. Как узнать, после возврата из SomeOtherFunction, остановлена машина или нет. Опять придётся лезть в исходный код SomeOtherFunction.

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

Продолжение следует…

Источник:
https://onurgumus.github.io/2022/12/26/Functional-Programming.html
День семьсот девятый.
Функциональное программирование. Окончание
Начало

Функциональное решение
Итак, чтобы избавиться от состояния, представим состояние с помощью типов. Таким образом, вместо Car, у нас будет два разных типа: StoppedCar и RunningCar. А также функция запуска, которая принимает StoppedCar и возвращает RunningCar. Таким образом мы решим все три проблемы:
SomeOtherFunction(RunningCar car){…}

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

Если ФП такое замечательное, почему оно не так популярно?
Во-первых, ФП - это не синтаксис, а парадигма. Например, недавно в C#9 появились записи. Запись - это функциональная концепция, и если вы не знакомы с функциональной парадигмой, вы можете не понимать, зачем они нужны. Можно сказать, что они неизменяемы, но это слишком упрощённо. На самом деле они обеспечивают семантику значений и ссылочную прозрачность, поэтому нас не волнует их расположение в памяти.

Я пытаюсь сказать, что для овладения ФП вы должны забыть о большинстве вещей, которые вы уже знаете в императивном программировании. И это одна из основных причин, по которой люди избегают ФП. В императивном мире есть инструкции, которые сообщают компилятору, что делать:
делай раз;
делай два;
Тогда как ФП – это как кадры фильма. Каждый кадр неизменяем, но, если мы меняем их 24 раза в секунду, создаётся иллюзия движущейся картинки. ФП делает то же самое.
фрейм1 -> функция преобразования -> фрейм2

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

Однако исторически проблемы с бОльшим потреблением ресурсов в ФП, позволили императивной парадигме процветать, а люди продолжали скептически относиться к ФП. Маленькое сообщество, меньше примеров, меньше ресурсов и инструментов. Статей, книг и руководств по ФП множество, но попробуйте найти функциональный пример приложения для простого списка дел с сохранением в БД. Или попробуйте использовать инструменты вроде ORM. Скорее всего вас ждёт разочарование и необходимость изучать новые способы сохранения данных в ФП.

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

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

Источник: https://onurgumus.github.io/2022/12/26/Functional-Programming.html
День семьсот десятый. #Оффтоп #КакСтатьСеньором
Что Я Узнал, Чтобы Стать Сеньором
Работник Bloomberg, пришедший туда джуниор-разработчиком, описывает свой опыт роста в компании.

6. Замечайте Странности
Как-то раз работал я с датой в Python. Это были даты, которые наша поисковая система должна была индексировать, и мы хотели, чтобы они были в формате UTC. Поэтому добавил преобразование даты в UTC перед загрузкой. Для этого нужно было добавить к датам часовой пояс. Я создал такие дату и время:
import datetime
from pytz import timezone
indexed_date =
datetime.datetime(2020, 11, 20,
12, 2, 0,
tzinfo=timezone('Asia/Kolkata'))

В моих тестах преобразованное время отличалось от ожидаемого на 23 минуты. Тогда я не придал этому значения, хотя это и смутило меня. Я просто поправил ожидаемое время на -23 минуты, чтобы тесты проходили. Да, я слышу, как вы осуждаете меня, это было однозначно хреновой идеей. Когда я это осознал, я уже не мог перестать думать об этом. До сих пор этот случай преследует меня. Как я мог позволить себе так «решить» проблему? Конечно, на код ревью кто-то из коллег прокомментировал это словами «это выглядит неправильно». Я был готов провалиться сквозь землю, и решил выяснить, что здесь пошло не так.

Это довольно серьёзная ошибка. В библиотеке Pytz есть историческая информация о часовых поясах. До 1942 года смещение для часового пояса Asia/Calcutta (Да, даже название города было другое) было +5:53:20. Когда часовой пояс из pytz передаётся в новую дату, библиотека не имеет информации о дате, чтобы выбрать актуальное на дату смещение, поэтому по умолчанию используется первое доступное смещение, что неверно. И в документации об этом упоминается. Правильный способ - использовать tzinfo.localize(), который выбирает часовой пояс соответственно дате:
from pytz import timezone
tz=timezone('Asia/Kolkata')
indexed_date = tz.localize(
datetime.datetime(2020, 11, 20,
12, 2, 0))

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

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

Источник: https://medium.com/better-programming/the-things-i-learned-to-become-a-senior-software-engineer-1083686d70cd
Автор оригинала – Neil Kakkar
День семьсот одиннадцатый. #MoreEffectiveCSharp
19. Избегайте Перегрузки Методов, Определённых в Базовых Классах
Когда базовый класс выбирает имя члена, он назначает определённый семантический смысл этому имени. Ни при каких обстоятельствах производный класс не должен использовать то же самое имя для других целей. И всё же есть много причин, по которым производный класс может захотеть использовать то же имя. Например, реализовать ту же семантику другим способом или с другими параметрами. Но вы не должны перегружать методы, объявленные в базовом классе.

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

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

Рассмотрим пару примеров:
public class Fruit { }
public class Apple : Fruit { }
Вот класс с методом, использующим производный параметр (Apple):
public class Animal {
public void Eat(Apple food) =>
WriteLine("Animal.Eat");
}
var obj1 = new Animal();
obj1.Eat(new Apple());
Понятно, что это выведет "Animal.Eat". Добавим производный класс с перегруженным методом с параметром базового типа:
public class Monkey : Animal {
public void Eat(Fruit food) =>
WriteLine("Monkey.Eat");
}
Итак, что выведет следующий код?
var obj2 = new Monkey();
obj2.Eat(new Apple());
obj2.Eat(new Fruit());

Оба вызова выведут "Monkey.Eat". Всегда в первую очередь вызывается метод производного класса, даже если в базовом классе есть более подходящий кандидат. Смысл в том, что автор производного класса лучше знает сценарий использования, поэтому производному методу отдаётся предпочтение. А если вот так:
Animal obj3 = new Monkey();
obj3.Eat(new Apple());
Смотрим внимательно: тип времени компиляции obj3 - Animal (базовый), хотя, во время выполнения тип будет Monkey (производный). Метод Eat не виртуальный, поэтому obj3.Eat() должен использовать Animal.Eat.

Больше ада! Добавим дженериков:
public class Animal {

public void Consume(IEnumerable<Apple> food) =>
WriteLine("Animal.Consume");
}
И перегрузку с коллекцией базового типа в производном классе:
public class Monkey : Animal {

public void Consume(IEnumerable<Fruit> food) =>
WriteLine("Monkey.Consume");
}
var food = new List<Apple> { new Apple(), new Apple() };
var obj2 = new Monkey();
obj2.Consume(food);

Что будет выведено на этот раз? Начиная с C#4.0 обобщённые интерфейсы поддерживают ковариантность и контравариантность. Это означает, что Monkey.Consume является кандидатом для IEnumerable<Apple>, хотя формально тип его параметра IEnumerable<Fruit>. Однако более ранние версии C# не поддерживают вариантности, и в них обобщённые параметры инвариантны. В этом случае единственным кандидатом будет Animal.Consume.

Да, вы можете удивить друзей на вечеринке программистов глубокими познаниями логики разрешения перегрузок в C#. Но не ожидайте, что пользователи вашего API будут иметь такие подробные знания, чтобы правильно использовать ваш API. Просто не перегружайте методы, объявленные в базовом классе. Это не представляет никакой ценности и только приведёт ваших пользователей в замешательство.

Источник: Bill Wagner “More Effective C#”. – 2nd ed. Глава 19.
День семьсот двенадцатый.
Используйте 2021й с Умом
Вот и настал первый рабочий день 2021го года. Судя по всему, он вряд ли будет сильно лучше 2020го. Как сделать так, чтобы по итогам года можно было сказать, что он не прошёл зря? Недавно вышло отличное видео на эту тему от Tim Corey. Подробности по ссылке в конце, а в посте приведу краткий перевод.

1. Задайте себе цель. Не призрачную, типа «хочу в будущем стать крутым разработчиком и получать много денег». Какую-то конкретную, достижимую к концу года: повысить свою квалификацию как разработчика в ХХХ. Для этого надо изучить сначала вот это, потом это, а затем вон то. Допустим, на первое вам потребуется 2 месяца, на второе ещё три, и т.п. Главное держать в уме цель и план движения к этой цели.

2. Пишите код. Неважно, сколько материала вы прочитаете, посмотрите или услышите. Пока вы это не попробуете, не столкнётесь с проблемами, и не решите их, материал не усвоится. Кроме того, вы ОБЯЗАТЕЛЬНО что-то упустите или неверно поймёте. И только практикой получится найти, что вы упустили, и исправить это.

Что Изучать .NET Разработчику в 2021м
Понятно, что для каждого индивидуально список может варьироваться, но далее будут 4 общих темы, которые подойдут всем.
1. C#
Как бы это банально ни звучало, стоит подтянуть базис. В какой бы сфере разработки вы ни были заняты, C# лежит в основе всего. И хорошее знание языка поможет всегда. Повторите темы, с которыми вы редко работаете. Подучите то, что давно откладывали.

2. Веб
Изучите основы HTML, CSS и Javascript, если ещё не сделали этого. Затем ASP.NET Core. Сюда входят все 5 основных типов проектов: MVC, Razor Pages, Web API, Blazor Server и Blazor WebAssembly. Изучите типы проектов, с которыми не работали.

3. Docker
Это уже стало обязательным к изучению для бизнес-разработчика. Умение быстро собрать и развернуть сервер с нужной конфигурацией для теста на своей машине или так же развернуть сервер с нужным для бизнеса сервисом – неоценимое качество разработчика, резко повышающее его эффективность.

4. Azure
Или другой облачный сервис, например, AWS. Всё больше компаний переходят в облако. А сами облачные провайдеры предлагают всё больше и больше различных полезных сервисов, которые могут использовать не только крупные компании, но и отдельные разработчики. Поэтому знания о том, как они работают, будут ценными в любой области разработки.

Что НЕ СТОИТ Изучать .NET Разработчику в 2021м
Со следующими темами нужно быть аккуратными. Вокруг них в последнее время много хайпа, но там могут оказаться серьёзные подводные камни.
1. Machine Learning
Этим уже прожужжали все уши. «Запишитесь на наши курсы, и всего за пару месяцев вы измените свою жизнь. Вас ждёт интересная работа, гарантии трудоустройства, а зарплаты специалистов машинного обучения начинаются со 100 тысяч рублей». Да, это очень интересно и круто. Только никто не говорит, что обычному человеку без серьёзного математического образования стать хорошим специалистом в машинном обучении будет довольно сложно.

2. Javascript Фреймворки для Фронтэнда
Angular, React, Vue – все очень интересные вещи. Но C# разработчику углублённо их изучать по принципу «чтоб было» не стоит. Эти фреймворки появляются, развиваются и исчезают очень быстро. И поддерживать актуальные знания о них – значит учиться постоянно, то есть уйти с головой во фронтэнд. Если вы уже хорошо знаете основы работы веб (см. выше), и только если вы посчитаете, что какой-то из этих фреймворков вам поможет в работе, тогда стоит его изучить.

3. Xamarin
Не потому, что это плохая платформа, просто это неудачный год для её изучения. В .NET 6 планируются серьёзные изменения в сфере мобильной разработки. И пока не понятно, в какую сторону они пойдут, и будут ли знания Xamarin актуальными.

4. Kubernetes
Так же, как со вторым пунктом. В Kubernetes используется так много различных технологий, которые постоянно меняются, что изучать его лучше только по необходимости, чтобы не потонуть в объёме информации.

Источник: https://www.youtube.com/watch?v=fFT5o4paUek
День семьсот тринадцатый. #Оффтоп #РазговорыПоВторникам
Эмоциональное Выгорание
Первый рабочий вторник, первый разговор по вторникам в этом году. И снова тему дал Сергей Немчинский. Эмоциональное выгорание. Что это и как с этим бороться.

Давайте я не буду пересказывать то, что рассказал Сергей. Просто посмотрите ролик по ссылке ниже и давайте обсудим. Кто сталкивался? Кто как с этим боролся? Может кто-то реально ходил к психологу?

Жду ваших комментариев.

Источник: https://www.youtube.com/watch?v=XCh2rdopsmc
День семьсот четырнадцатый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
73. Простота от Сокращения
«Переделай это…», - сказал мне босс, нажимая и удерживая клавишу Delete. Я смотрел на экран компьютера со знакомым многим ощущением стыда и безысходности, поскольку мой код - строка за строкой - исчезал в небытии.

Мой босс, Стефан, не отличался красноречием, но он мог опознать плохой код, когда видел его. И он точно знал, что с ним делать.

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

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

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

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

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

Конечно, если реорганизация не помогает, просто удалите всё и напишите код заново. Такое воссоздание с нуля часто помогает избавить код от значительной части лишнего мусора.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Paul W. Homer
День семьсот пятнадцатый. #TypesAndLanguages
Довольно интересной показалась серия статей Types and Programming Languages от Adam Furmanek. Интересные наблюдения и тонкости языков программирования. Сегодня первая часть.

1. Не используйте return в блоке finally
Многие языки предоставляют конструкцию обработки исключений, обычно в виде блоков try и catch, хотя детали реализации различаются. Программисты обычно предполагают, что эти конструкции работают одинаково для разных языков. К сожалению, это неправда, и нюансы часто бывают сложны для понимания, когда дело доходит до крайних случаев.

Некоторые языки поддерживают дополнительный блок finally, который должен выполняться «несмотря ни на что» - независимо от того, было сгенерировано исключение или нет. Это, правда, не совсем так: есть много ситуаций, когда этот блок не может быть вызван, например, аварийное завершение приложения, ошибки нарушения доступа и т. д. Но сейчас не об этом.

Некоторые языки позволяют возвращать результат из блока finally. В следующем примере на Java последний return «побеждает» другие:
public static void main (String[] args)
throws java.lang.Exception {
System.out.println(foo());
}
public static int foo(){
try{
return 5;
}finally{
return 42;
}
}

Результат - 42, потому что это последнее возвращаемое значение. Такое же поведение можно видеть в Python, JS, возможно, и на других платформах. Примечательным исключением здесь является C#, который не позволяет использовать return в блоке finally, просто чтобы избежать этой путаницы.

Результат кажется простым и логичным. Но что произойдет, если вы выбросите исключение, а затем используете return? Например, в Java:
public static void main (String[] args)
throws java.lang.Exception {
System.out.println(foo());
}
public static int foo() {
try {
throw new RuntimeException(
"Это сообщение пропадает");
}finally{
return 42;
}
}

Результат – всё равно 42. Исключение «проглатывается». То же самое в JS:
function foo(){
try{
throw "Это сообщение пропадает";
}finally{
return 42;
}
}
console.log(foo());

Python:
def foo():
try:
raise Exception("Это сообщение пропадает")
finally:
return 42

print(foo())

В общем случае, никогда не используйте return в блоке finally. Это ломает механизм обработки исключений.

Источник: https://blog.adamfurmanek.pl/2021/01/09/types-and-programming-languages-part-1/
День семьсот шестнадцатый.
10 Библиотек C#, Которые Сохранят Ваше Время и Энергию
Интересная подборка от Tim Corey библиотек и инструментов, которые серьёзно упростят вашу жизнь.

1. SharpZipLib http://icsharpcode.github.io/SharpZipLib/
NuGet пакет для архивации на C#, который может быть использован в ваших проектах. Поддерживает zip, gzip, bzip2 и tar форматы. Может создавать архивы, читать, извлекать файлы из них и т.п.

2. FluentEmail https://github.com/lukencode/FluentEmail
Простой пакет для отправки email из .NET и .NET Core. Использует Razor для шаблонов email и может отправлять письма через SendGrid, MailGun, SMTP и т.д. Слово «простой» здесь не следует путать с «примитивный». Инструмент достаточно мощный. Но в общем случае, если вам нужно просто отправлять email из вашего приложения на C#, FluentEmail - то, что вам нужно.

3. MailKit https://github.com/jstedfast/MailKit
А вот когда нужна тонкая настройка отправки (аутентификация, прокси, настройка SMTP/POP3/IMAP клиентов и т.п.), вам поможет MailKit.

4. Papercut SMTP https://github.com/ChangemakerStudios/Papercut-SMTP
Ещё один инструмент для работы с электронной почтой. Это простой локальный SMTP сервер для тестирования отправки почты. С помощью Papercut SMTP вы можете просматривать и инспектировать всю почту, которую отправляет ваше приложение, включая просмотр заголовков и исходного кода письма.

5. EPPlus https://github.com/EPPlusSoftware/EPPlus
Пакет для чтения и записи файлов Excel из вашего кода C#. Требует лицензии для коммерческого использования, но для разработки можно использовать бесплатно.

6. Hangfire https://www.hangfire.io/
Инструмент, помогающий вам запускать фоновые задания в .NET. В бесплатной версии поддерживает обычный запуск фоновой задачи, отложенный запуск, выполнение по расписанию, выполнение задачи в продолжение другой задачи. В платной версии можно запускать пакеты задач. Кроме того, у Hangfire есть удобная панель мониторинга выполнения задач.

7. MassTransit https://masstransit-project.com/
Слой абстракции для использования инструментов очередей, вроде Azure Service Bus или RabbitMQ.

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

8. Polly https://github.com/App-vNext/Polly
Пакет предназначен для осуществления повторных попыток. Если у вас есть API, который может быть недоступен какое-то время, вам необходимо реализовать механизм повторения запросов, и Polly очень облегчит вам жизнь.

9. Serilog https://serilog.net/
Пакет, практически ставший стандартом де-факто для ведения структурированных логов в .NET.

10. Seq https://datalust.co/seq
Этот инструмент поможет вам разобраться в логах, созданных, например, тем же Serilog. Он предоставляет панель управления для визуализации и быстрой фильтрации информации, сохранённой в логах. С помощью Seq вы можете посмотреть, как часто возникает то или иное событие, найти все записи в логах по заданным параметрам и т.п.

Источник: https://www.youtube.com/watch?v=uS0hRJqamfU
День семьсот семнадцатый. #Оффтоп #КакСтатьСеньором
Что Я Узнал, Чтобы Стать Сеньором
Работник Bloomberg, пришедший туда джуниор-разработчиком, описывает свой опыт роста в компании.

7. Станьте Мультипликатором Силы
В один прекрасный спринт я случайно почувствовал мощь Силы.
«Сила — это то, что даёт джедаю его могущество. Это энергетическое поле, создаваемое всеми живыми существами. Она окружает и пронизывает нас. Она связывает Вселенную воедино».
– Оби-Ван Кеноби, «Звездные войны»

Я думаю, что Оби-Ван Кеноби познал истину, хотя и не в той области. Это то, что я могу использовать в разработке программного обеспечения: стать мультипликатором силы.

В том спринте я сам многого не добился. Я написал совсем чуть-чуть кода. Зато я координировал, какие изменения должны выйти, когда (это был сложный спринт), тестировал изменения, делал много обзоров кода, вносил альтернативные предложения по дизайну и программировал в паре, когда это требовалось, и когда кто-то застревал, чтобы двигать весь процесс вперёд. Мы всё сделали, и в этом случае отход от деталей и более общий взгляд на проект помог мне легче принимать управленческие решения. Это был один из наших самых продуктивных спринтов.

«Сила — это то, что даёт разработчику его могущество. Это энергетическое поле, создаваемое всеми разработчиками и их инструментами. Она окружает и пронизывает нас. Она связывает код воедино».

Ладно, хватит про «Звёздные войны».

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

Положительные примеры культуры умножают Силу. Я смог сделать то, что сделал, потому что наша культура позволяла это. Наша командная культура оценивает в спринте результаты команды, а не результаты отдельных разработчиков. Это позволило мне оптимизировать работу команды вместо того, чтобы сосредоточиться на себе. Команда формирует культуру, а культура формирует команду. Эта идея распространяется даже на города и народы:
«В обществе, существующем в условиях постоянной внешней военной угрозы, будут высоко цениться военные и боевые качества. В обществе с экономикой, основанной на совместном труде, будет осуждаться лень. В обществе с равными правами властность будет считаться отрицательной чертой характера. В индустриальном обществе с регламентированным рабочим графиком будет цениться пунктуальность, и т.д.».
– Иэн Бэнкс «Почему Культура побеждает»

Источник: https://medium.com/better-programming/the-things-i-learned-to-become-a-senior-software-engineer-1083686d70cd
Автор оригинала – Neil Kakkar
День семьсот восемнадцатый. #ЗаметкиНаПолях
Делаем Заглавными Первые Буквы Слов
Работа над презентацией – значительная часть разработки ПО. Мы тратим на форматирование строк больше времени, чем хотелось бы.

Регистр заголовка - это использование первой заглавной буквы в каждом слове в строке, оставляя остальные буквы строчными. Конечно, можно это сделать вручную, но для этого в .NET есть специальный объект.

Большинство элементов отображения текста не универсальны для .NET, а управляются экземплярами типа CultureInfo, хранящими настройки языка и региональных параметров. Экземпляры CultureInfo определяют стили даты, валюты и т.п. В CultureInfo также вложен класс TextInfo, который определяет систему написания слов в культуре. Его метод ToTitleCase можно использовать для преобразования текста в регистр заголовка.

using static System.Console;
using static System.Globalization.CultureInfo;

var сulture = GetCultureInfo("en-gb");
var info = сulture.TextInfo;

var name = ".NET is aWESome";

WriteLine(info.ToTitleCase(name));
//Вывод: .NET Is Awesome

Заметьте, что аббревиатура .NET оставлена в изначальном виде, тогда как прописные буквы в слове "aWESome" заменены строчными.

Остаётся одна проблема. По правилам английского написания слово «is» не должно начинаться с заглавной буквы. Аналогично в других языках предлоги и союзы должны оставаться строчными. Например:
Немецкий
Исходная строка: Per anhalter durch die galaxis
Ожидаемый результат: Per Anhalter durch die Galaxis
Реальный результат: Per Anhalter Durch Die Galaxis

Французский
Исходная строка: les naufragés d'ythaq
Ожидаемый результат: Les Naufragés d'Ythaq
Реальный результат: Les Naufragés D'ythaq

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

Источник: https://khalidabuhakmeh.com/capitalize-first-letter-of-words-with-csharp
День семьсот девятнадцатый. #ЧтоНовенького
Более интегрированный терминал
В Visual Studio 2019 v16.8 добавили несколько новых фишек во встроенный терминал.

Интеграция с Обозревателем Решений
Новая команда контекстного меню позволяет открыть терминал по определенному пути. Выберите решение, проект или папку, нажмите правой кнопкой мыши и выберите Open in TerminalОткрыть в терминале»). См. картинку 1.

Команды Панели Инструментов
На панели инструментов окна терминала добавлены кнопки для копирования и вставки, а также кнопка настроек. См. картинку 2. Во многих терминалах Ctrl+C используется для прерывания текущей операции. Поэтому по умолчанию для копирования и вставки используются Ctrl+Shift+C и Ctrl+Shift+V. Однако их можно настроить. Перейдите в Options > Environment > Keyboard (Опции > Настройки среды > Клавиатура) и отредактируйте горячие клавиши для команд Terminal.Copy и Terminal.Paste.

Источник: https://devblogs.microsoft.com/visualstudio/a-more-integrated-terminal-experience/
День семьсот двадцатый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
74. Принцип Единственной Ответственности
Вспомним SOLID. Один из основных принципов хорошего дизайна ПО гласит:
Соберите вместе то, что меняется по одной и той же причине, и разделите то, что меняется по разным причинам.

Этот принцип часто называют принципом единственной ответственности или SRP. Более строго, в нём говорится, что подсистема, модуль, класс или даже функция не должны иметь более одной причины для изменения. Классическим примером является класс, в котором есть методы, которые работают с бизнес-правилами, отчётами и базой данных:
public class Employee {
public double CalculatePay() …
public string ReportHours() …
public void Save() …
}

На первый взгляд объединение этих трёх методов в одном классе совершенно уместно. В конце концов, классы и должны быть наборами функций, которые работают с общими данными. Однако проблема в том, что эти три метода меняются по совершенно разным причинам. Метод CalculatePay будет меняться всякий раз, когда меняются бизнес-правила расчёта оплаты. Метод ReportHours будет меняться, когда кто-то захочет изменить формат отчёта. Метод Save будет изменяться всякий раз, когда администраторы баз данных изменяют схему базы данных. Эти три причины для изменений в совокупности делают класс Employee очень нестабильным. Он будет изменяться по любой из этих причин. Что ещё более важно, эти изменения повлияют на любые классы, которые зависят от Employee.

Хороший дизайн системы означает, что мы разделяем систему на компоненты, которые можно развернуть независимо. Независимое развёртывание означает, что, если мы изменим один компонент, нам не придется повторно развёртывать какие-либо другие. Однако, если Employee активно используется многими другими классами в других компонентах, то каждое изменение в Employee, вероятно, приведет к повторному развёртыванию других компонентов, тем самым сводя на нет главное преимущество компонентного дизайна. Следующее простое разделение решает эту проблему:
public class Employee {
public double CalculatePay()…
}
public class EmployeeReporter {
public string ReportHours(Employee e)…
}
public class EmployeeRepository {
public void Save(Employee e)…
}

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

Внимательный читатель увидит, что в приведенном выше решении всё ещё есть зависимости. Классы EmployeeReporter и EmployeeRepository по-прежнему зависят от Employee. Поэтому, если Employee будет изменён, их, вероятно, придётся перекомпилировать и повторно развернуть. Таким образом, Employee всё ещё нельзя изменить, а затем развернуть независимо. Однако другие классы можно. Никакая модификация в EmployeeReporter или EmployeeRepository не может привести к необходимости перекомпиляции или повторного развёртывания других классов. И даже Employee можно было бы развернуть независимо за счет осторожного использования принципа инверсии зависимостей.

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

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Robert C. Martin
День семьсот двадцать первый. #TypesAndLanguages
2. Исключение при Обработке Исключения
В прошлый раз мы рассмотрели, что не стоит использовать return в блоке finally. Сегодня исследуем аналогичный случай исключения при обработке исключения:
try{
try{
throw new Exception("Exception 1");
}finally{
throw new Exception("Exception 2");
}
}catch(Exception e){
Console.WriteLine(e);
}

Что будет выведено? Этот вопрос с подвохом. Во-первых, есть два исключения, и мы знаем, что некоторые языки (включая платформу .NET) реализуют двухпроходную систему обработки исключений. Первый проход по стеку ищет обработчик, способный обработать исключение, затем второй проход раскручивает стек и выполняет все блоки finally. Но что, если мы выбросим исключение во втором проходе?

Результат различается в разных языках. Например, C# теряет исключение, что указано в спецификации:
Если блок finally выбрасывает другое исключение, обработка текущего исключения прекращается.

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

Это важно при работе с ресурсами. Некоторые языки предоставляют конструкцию типа using в C#:
using(var resource = new Resource()){
//…
}
Концептуально это аналогично следующему коду:
var resource = new Resource();
try{
//…
} finally{
if(resource != null) resource.Dispose();
}

Кажется довольно просто. Но что если Dispose выбросит исключение:
public class Resource : IDisposable {
public void Dispose(){
Console.WriteLine("Disposing");
throw new Exception("Disposing failed");
}
}

Тогда следующий код:
try{
using(var resource = new Resource()){
Console.WriteLine("Using");
throw new Exception("Using failed");
}
}catch(Exception e){
Console.WriteLine("Exception: " + e);
}

выведет
Using
Disposing
Exception: System.Exception: Disposing failed
at Resource.Dispose()
at Program.Main()

Мы теряем исключение "Using failed" из-за нового исключения "Disposing failed" в блоке finally. Кстати, в Java это реализовано правильно, и сохраняет оба исключения.

Источники:
-
https://blog.adamfurmanek.pl/2021/01/16/types-and-programming-languages-part-2/
-
https://blog.adamfurmanek.pl/2020/07/25/net-inside-out-part-21/
День семьсот двадцать второй. #Оффтоп
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь первая.

Интересное видео – очередная серия подкаста Solo on .NET от Дмитрия Нестерука о языках программирования. Хотел написать, что подойдёт новичкам для выбора языка. Но потом вспомнил количество технологий и узкоспециализированных терминов, о которых говорит Дмитрий, и понял, что новичок послушает, ужаснётся и вообще откажется от затеи связать свою жизнь с программированием)))

В общем, не совсем для новичков будут интересны рассуждения о выборе языка: если вы не знаете, о чём это, и боитесь спросить. Для совсем не новичков просто сравнительный анализ языков и технологий, а также немного «за жизнь». Мне понравилось.
День семьсот двадцать третий. #Оффтоп
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь вторая. См. вещь первая.

Разговор зашёл о вопросах на собеседовании и, в частности, про TAP (Task-based Asynchronous Pattern), про которую очень любят спрашивать, например, в Epam. Поскольку тема обширная и запутанная, или, цитируя один из источников ниже: «При попытках понять асинхронность наблюдается чередование состояний "теперь понятно" и "теперь снова непонятно".», - в этом посте сохраню несколько источников для изучения темы.

1. Книга «Конкурентность в C#» Стивена Клири
Как обычно, выкладываю ссылку только на магазин, кто захочет нахаляву, тот найдёт. Сам я её пока не читал, но те, кто читал, рекомендуют. Однако, она не для изучения темы с нуля. Поэтому для начала можно почитать следующее.

2. «The Task-based Asynchronous Pattern» Stephen Toub, Microsoft, 2012
Статья около 35 страниц. Довольно старая, но концептуально ничего не поменялось.

3. «Async/await в C#: концепция, внутреннее устройство, полезные приемы»
Полезная статья, возможно немного сумбурная, потому что сжато. Мне показалось, что это вольный пересказ 5й главы книги «С# In Depth» Джона Скита. Для любителей читать в печатном варианте, см. вложение ниже.

4. Также скромно порекомендую полистать посты на канале, например, по тегу #AsyncAwaitFAQ.