Уютное сообщество C# разработчиков
2.51K subscribers
44 photos
53 links
Уютное сообщество C# - обучающий канал для разработчиков.

Полезные материалы по языку программирования.
Тесты на знание C#
Подсказки и трюки языка

@aldrson @viktorreh
加入频道
Навигация по тегам:

Вакансии -
#вакансия
Статьи -
#полезное
Трюки и возможности языка -
#tips
Тесты -
#тест
Книги -
#книги
1
Факт дня про C# 11

Допустим, в проекте подключены nullable reference types.
Затем, объявляется некоторый класс с полем, у которого notnull ссылочный тип.
Чтобы не получить от компилятора warning CS8618, многие делают так:

public class Foo
{
public string Bar {get; set;} = null!;
}

В C# 11 появилось ключевое слово required, которое позволяет красиво обыграть эту ситуацию:

public class Foo
{
public required string Bar {get; set;}
}

#полезное #tips
3 причины использовать DateTimeOffset вместо DateTime

Если что-то из этого вам подходит стоит задуматься о смене используемого типа данных 🤔

Необходимо проверить, что из следующего вашему приложению нужно:

1️⃣ Уникально и однозначно определять единый момент во времени.
В системе появится недвусмысленное понятие «сейчас», можно будет логгировать временные метки транзакций, событий, созданий, модификаций и так далее.

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

3️⃣ Хранение нескольких связанных между собой меток времени, как частей одной структуры или одного массива данных.

#полезное #tips
🔥8👍7👎3💩2🤮1
Самый редко используемый цикл в C#

Цикл do while редко используется, но важно помнить о его существовании, чтобы понимать возможные случаи применения.

Его синтаксис таков:

do
{
// тело цикла
} while (condition)

Этот цикл всегда исполняет своё тело как минимум один раз, даже если условие не выполнено.

Из этой особенности, отличающей do while от других видов циклов, вытекают его основные сценарии использования.

А как часто вы используете do while?

#полезное #tips
👍12💩2😢1
"Стоит ли использовать foreach, если for быстрее?"

Здесь важно понимать, чем отличаются эти циклы.

▪️for - это классический управляющий оператор, который транслируется в +/- одинаковый IR или машинный код практически во всех языках программирования согласно блок-схеме выше.

Принцип его работы одинаков везде:
for (выражение1; выражение2; выражение3)
оператор

▪️foreach
- это синтаксический сахар над итераторами, который ещё и работает согласно принципам утиной типизации.
Перебираемому объекту даже не обязательно реализовывать IEnumerable.

Разворачивается такой цикл примерно в следующее:
var enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
// ...
}

Невооружённым глазом видно, что здесь происходит вызов через VMT, возможен cast, выделение памяти и так далее.
Обслуживание foreach очевидно дороже, потому что это абстракция.

Так что использование везде for вместо foreach - скорее микрооптимизация.

#полезное #tips
👍8🔥42
В последнее время часто приходится работать с enum.

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

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

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

Иначе default(TEnum) может вернуть первое значение из перечисления, что может приводить к логическим несостыковкам и смысловым ошибкам.

Конечно, так надо делать только в соответствии с описываемой предметной областью.
Такое значение может называться None, Default или Unknown.

Такая структура enum позволит описать, как работать с вашим пользовательским типом данных.

#полезное #tips
👍22
Возможно вы не знали этот метод LINQ

С 6-ой версии в .NET присутствует метод Chunk, который позволяет дробить коллекцию на одинаковые кусочки, так называемые "чанки".

// получаем итерируемый объект
var enumerable = Enumerable.Range(1, 999);

// дробим на чанки
var chunks = enumerable.Chunk(29);

// итерируем по чанкам
foreach(var chunk in chunks) // для каждого чанка
{
foreach(var item in chunk) // для каждого элемента в чанке
{
Console.WriteLine(item);
}
}

Пользуйтесь на здоровье!

#полезное #tips
🔥25👍122
Как генерировать валидные данные?

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

А вот и нет! В сегодняшнем разговоре о генерации данных ключевое слово валидные.

Ответ на вопрос в заголовке: Bogus.

Да, для C# существует не один генератор данных.
Но разница проявляется как в назначении, так и в устройстве.

AutoFixture - это скорее некоторое подобие DI контейнера с CQRS архитектурой.
Bogus - инструмент, больше похожий на FluentValudation.

Для каждой модельки создаётся специальный генератор.
В нём с помощью fluent interface api всем полям прописываются правила по заполнению значений.

public class OrderFaker : Faker<Order>
{
public OrderFaker()
{
RuleFor(o => o.OrderId, f => f.Guid.NewGuid());
RuleFor(o => o.Item, f => f.Lorem.Sentence());
RuleFor(o => o.Quantity, f => f.Random.Number(1, 10));
}
}

var orderFaker = new OrderFaker();
var order = orderFaker.Generate();

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

#полезное #tips
👍8
Какой длины должны быть функции?

Если обратиться к такой классике, как «Чистый Код» Роберта С. Мартина, то можно увидеть следующее:

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

То есть, кажется, что это означает примерно следующее:

1️⃣ Функции должны быть короткими — не длиннее 20 строк и в большинстве случаев менее 10 строк.

2️⃣ Функции должны иметь как можно меньше аргументов, желательно ни одного.

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

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

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

Однако, практика показывает, что в любом проекте можно найти большой объём кода под рефакторинг с методами и на 100, 200, 300 строк с десятком параметров.

Как вы думаете, стоит их распиливать и переписывать?

#полезное #tips
💯6👍4
Как лучше регистрировать коллекцию зависимостей?

Представим, что вам нужно внедрить коллекцию из сервисов типа IMyService. Тогда вы ожидаете её вот так:

class MyOtherService : IMyOtherService
{
public MyOtherService(IEnumerable<IMyService> myServices)
{
//...
}
}

Что приходит на ум?

▪️Создать свою кастомную коллекцию, и зарегистрировать её.

▪️Буквально зарегистрировать объект списка или массива с набором нужных зависимостей

Однако можно поступить гораздо проще и просто регистрировать зависимости как обычно. Например:

services.AddScoped<IMyService, MyService>();

DI контейнер всё поймёт и даже если будет зарегистрирована всего одна реализация, она будет представлена в коллекции.

#полезное #tips
👍1🔥1
Главное противоречие ООП

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

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

Эта ситуация называется InterfaceImplementationPair.

В самом деле привычка восходит корнями к C/C++, где принято разделять header и code файлы.

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

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

Поэтому не стоит бояться отсутствия интерфейсов, код от этого не потеряет в чистоте.

#полезное #tips
👍42