Факт дня про C# 11
Допустим, в проекте подключены
Затем, объявляется некоторый класс с полем, у которого
Чтобы не получить от компилятора
Допустим, в проекте подключены
nullable reference types
.Затем, объявляется некоторый класс с полем, у которого
notnull
ссылочный тип.Чтобы не получить от компилятора
warning CS8618
, многие делают так:public class FooВ C# 11 появилось ключевое слово
{
public string Bar {get; set;} = null!;
}
required
, которое позволяет красиво обыграть эту ситуацию:public class Foo#полезное #tips
{
public required string Bar {get; set;}
}
3 причины использовать
Необходимо проверить, что из следующего вашему приложению нужно:
1️⃣ Уникально и однозначно определять единый момент во времени.
В системе появится недвусмысленное понятие «сейчас», можно будет логгировать временные метки транзакций, событий, созданий, модификаций и так далее.
2️⃣ Выполнение общих арифметических операций над датой и временем с высокой точностью.
Например, прибавляете к некоторой дате шесть месяцев, и ожидаемый результат должен получиться с поправкой на летнее время.
3️⃣ Хранение нескольких связанных между собой меток времени, как частей одной структуры или одного массива данных.
#полезное #tips
DateTimeOffset
вместо DateTime
Если что-то из этого вам подходит стоит задуматься о смене используемого типа данных 🤔Необходимо проверить, что из следующего вашему приложению нужно:
1️⃣ Уникально и однозначно определять единый момент во времени.
В системе появится недвусмысленное понятие «сейчас», можно будет логгировать временные метки транзакций, событий, созданий, модификаций и так далее.
2️⃣ Выполнение общих арифметических операций над датой и временем с высокой точностью.
Например, прибавляете к некоторой дате шесть месяцев, и ожидаемый результат должен получиться с поправкой на летнее время.
3️⃣ Хранение нескольких связанных между собой меток времени, как частей одной структуры или одного массива данных.
#полезное #tips
🔥8👍7👎3💩2🤮1
Самый редко используемый цикл в C#
Цикл
Его синтаксис таков:
Из этой особенности, отличающей
А как часто вы используете
#полезное #tips
Цикл
do while
редко используется, но важно помнить о его существовании, чтобы понимать возможные случаи применения.Его синтаксис таков:
do
{
// тело цикла
} while (condition)
Этот цикл всегда исполняет своё тело как минимум один раз, даже если условие не выполнено.Из этой особенности, отличающей
do while
от других видов циклов, вытекают его основные сценарии использования.А как часто вы используете
do while
?#полезное #tips
👍12💩2😢1
"Стоит ли использовать
Здесь важно понимать, чем отличаются эти циклы.
Принцип его работы одинаков везде:
Перебираемому объекту даже не обязательно реализовывать
Разворачивается такой цикл примерно в следующее:
Обслуживание
Так что использование везде
#полезное #tips
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🔥4❤2
В последнее время часто приходится работать с
Многим известно, что
Но сегодня хочется рассказать о хорошей, как мне кажется, практике проектирования перечисления.
Создавая новое перечисление, старайтесь предоставлять значение по умолчанию.
Иначе
Конечно, так надо делать только в соответствии с описываемой предметной областью.
Такое значение может называться
#полезное #tips
enum
.Многим известно, что
enum
это тип значения, определённый набором именованных констант, в каких случаях его удобно использовать и так далее.Но сегодня хочется рассказать о хорошей, как мне кажется, практике проектирования перечисления.
Создавая новое перечисление, старайтесь предоставлять значение по умолчанию.
Иначе
default(TEnum)
может вернуть первое значение из перечисления, что может приводить к логическим несостыковкам и смысловым ошибкам.Конечно, так надо делать только в соответствии с описываемой предметной областью.
Такое значение может называться
None
, Default
или Unknown.
Такая структура enum
позволит описать, как работать с вашим пользовательским типом данных.#полезное #tips
👍22
Возможно вы не знали этот метод LINQ
С 6-ой версии в .NET присутствует метод
#полезное #tips
С 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👍12❤2
Как генерировать валидные данные?
Активные читатели моего канала могут подумать, что сейчас будет очередной пост про какую-нибудь кастомизацию AutoFixture, ведь все знают, как библиотека генерирует строки и числа.
А вот и нет! В сегодняшнем разговоре о генерации данных ключевое слово валидные.
Ответ на вопрос в заголовке: Bogus.
Да, для C# существует не один генератор данных.
Но разница проявляется как в назначении, так и в устройстве.
AutoFixture - это скорее некоторое подобие DI контейнера с CQRS архитектурой.
Bogus - инструмент, больше похожий на FluentValudation.
Для каждой модельки создаётся специальный генератор.
В нём с помощью
В арсенале богуса есть адреса, имена, различные локали, схемы генерации, расширяемость и многое другое.
#полезное #tips
Активные читатели моего канала могут подумать, что сейчас будет очередной пост про какую-нибудь кастомизацию 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
Если обратиться к такой классике, как «Чистый Код» Роберта С. Мартина, то можно увидеть следующее:
1️⃣ Функции должны быть короткими — не длиннее 20 строк и в большинстве случаев менее 10 строк.
2️⃣ Функции должны иметь как можно меньше аргументов, желательно ни одного.
Стоит придерживаться стратегии «разделяй и властвуй».
Попробовать разделить метод на несколько логических шагов, определить что от чего зависит.
Затем, разбить это на минимальные модули, которые соединяются в единый механизм, например, с помощью внедрения зависимостей.
Такие действия, безусловно, помогут начертить границы ответственностей, сделают код чище и читабельнее.
Однако, практика показывает, что в любом проекте можно найти большой объём кода под рефакторинг с методами и на 100, 200, 300 строк с десятком параметров.
Как вы думаете, стоит их распиливать и переписывать?
#полезное #tips
💯6👍4
Как лучше регистрировать коллекцию зависимостей?
Представим, что вам нужно внедрить коллекцию из сервисов типа
▪️Создать свою кастомную коллекцию, и зарегистрировать её.
▪️Буквально зарегистрировать объект списка или массива с набором нужных зависимостей
Однако можно поступить гораздо проще и просто регистрировать зависимости как обычно. Например:
#полезное #tips
Представим, что вам нужно внедрить коллекцию из сервисов типа
IMyService
. Тогда вы ожидаете её вот так:class MyOtherService : IMyOtherServiceЧто приходит на ум?
{
public MyOtherService(IEnumerable<IMyService> myServices)
{
//...
}
}
▪️Создать свою кастомную коллекцию, и зарегистрировать её.
▪️Буквально зарегистрировать объект списка или массива с набором нужных зависимостей
Однако можно поступить гораздо проще и просто регистрировать зависимости как обычно. Например:
services.AddScoped<IMyService, MyService>();DI контейнер всё поймёт и даже если будет зарегистрирована всего одна реализация, она будет представлена в коллекции.
#полезное #tips
👍1🔥1
Главное противоречие ООП
Зачастую многие разработчики сталкиваются с популярным заблуждением в коммерческой разработке.
Оно заключается в том, что для каждого класса, который реализует какую-то логику должен быть свой интерфейс.
Эта ситуация называется InterfaceImplementationPair.
В самом деле привычка восходит корнями к C/C++, где принято разделять header и code файлы.
Однако, время ушло далеко вперёд и надобность в подобных действиях сегодня отсутствует.
Наличие интерфейсов в отсутствие полиморфного поведения, мокирования или потребности в поставке контракта наружу через библиотеку лишь увеличивает стоимость сопровождения кода и количество занимаемых байтов на диске хранилища кодовой базы.
Поэтому не стоит бояться отсутствия интерфейсов, код от этого не потеряет в чистоте.
#полезное #tips
Зачастую многие разработчики сталкиваются с популярным заблуждением в коммерческой разработке.
Оно заключается в том, что для каждого класса, который реализует какую-то логику должен быть свой интерфейс.
Эта ситуация называется InterfaceImplementationPair.
В самом деле привычка восходит корнями к C/C++, где принято разделять header и code файлы.
Однако, время ушло далеко вперёд и надобность в подобных действиях сегодня отсутствует.
Наличие интерфейсов в отсутствие полиморфного поведения, мокирования или потребности в поставке контракта наружу через библиотеку лишь увеличивает стоимость сопровождения кода и количество занимаемых байтов на диске хранилища кодовой базы.
Поэтому не стоит бояться отсутствия интерфейсов, код от этого не потеряет в чистоте.
#полезное #tips
👍4❤2
Как скрыть от потребителя библиотеки лишние зависимости?
Недавно, я написал небольшую надстройку над AutoFixture, которая позволяет при создании объекта базового абстрактного класса получить экземпляр случайного наследника.
Допустим, у нас есть подобная иерархия:
Ожидается, что мы получим случайным образом либо
Однако, базовый функционал библиотеки не даёт такого поведения из коробки.
С помощью моего NuGet пакета можно настроить
В связи с тем, что она создана как дополнение к AutoFixture, присутствует зависимость, которая потом попадает к потребителю в раздел Implicitly Installed Packages.
Согласитесь, неприятно.
Решается проблема очень просто, достаточно пометить зависимость атрибутом
Недавно, я написал небольшую надстройку над AutoFixture, которая позволяет при создании объекта базового абстрактного класса получить экземпляр случайного наследника.
Допустим, у нас есть подобная иерархия:
abstract class A { }Если написать
class B : A { }
class C : A { }
fixture.Create<A>()
, то мы получим исключение, поскольку вызывать new для абстрактных классов запрещено.Ожидается, что мы получим случайным образом либо
new B()
, либо new C()
.Однако, базовый функционал библиотеки не даёт такого поведения из коробки.
С помощью моего NuGet пакета можно настроить
fixture
таким образом, и всё заработает:fixture.CustomizePolymorphism<A>()Так вот при создании библиотеки возникла проблема.
.WithDerivedType<B>()
.WithDerivedType<C>()
.BuildCustomization();
В связи с тем, что она создана как дополнение к AutoFixture, присутствует зависимость, которая потом попадает к потребителю в раздел Implicitly Installed Packages.
Согласитесь, неприятно.
Решается проблема очень просто, достаточно пометить зависимость атрибутом
PrivateAssets
со значением all
в конфигурации файла проекта:<PackageReference Include="AutoFixture" Version="4.18.0" PrivateAssets="all"/>#полезное #tips
👍4🤔1
Что такое .NET?
.NET это фреймворк (платформа), написанный компанией Microsoft для создания множества разных типов приложений. Почему фреймворк назвали именно так, до сих пор до конца не ясно.
.NET позволяет работать со множеством языков - C#, F#, Visual Basic. C#, как вы поняли самый популярный.
На сегодняшний день, современным видением платформы то, что выросло из .NET Core. То есть, курс на кросс-платформенность и отсутствие привязки к операционной системе Windows. Тут уже и приложения быстрее, и можно собрать приложение из микросервисов в Docker контейнерах.
Что можно сделать с помощью .NET?
Веб, мобилки, десктоп, ML, геймдев, IoT и так далее.
Но люди все равно выберут Java, поэтому пожелаем роста платформе!
Если хотите узнать больше о платформе .NET, поделитесь этим в комментариях!
#полезное #tips
.NET это фреймворк (платформа), написанный компанией Microsoft для создания множества разных типов приложений. Почему фреймворк назвали именно так, до сих пор до конца не ясно.
.NET позволяет работать со множеством языков - C#, F#, Visual Basic. C#, как вы поняли самый популярный.
На сегодняшний день, современным видением платформы то, что выросло из .NET Core. То есть, курс на кросс-платформенность и отсутствие привязки к операционной системе Windows. Тут уже и приложения быстрее, и можно собрать приложение из микросервисов в Docker контейнерах.
Что можно сделать с помощью .NET?
Веб, мобилки, десктоп, ML, геймдев, IoT и так далее.
Но люди все равно выберут Java, поэтому пожелаем роста платформе!
Если хотите узнать больше о платформе .NET, поделитесь этим в комментариях!
#полезное #tips
👍16🔥1
Freeze
/Inject
/Register
в AutoFixtureДавным-давно, в первых версиях библиотеки балом правил
Register
. Два других метода ещё не существовали.Определён он был следующим образом:
public static void Register<T>(this IFixture fixture, T item)
Однако, была и перегрузка:public static void Register<T>(this IFixture fixture, Func<T> creator)
Какое-то время казалось, что это нормально, впрочем, вскоре пользователи стали путаться. Например, можно было написать следующий код:fixture.Register(() => universe.LightUp());
Поскольку делегат может получаться путём приведения ссылкой на метод, то валидной была и следующая запись:fixture.Register(universe.LightUp);
Что если universe.LightUp
это обращение к свойству, а не к методу? Тогда была бы выбрана первая перегрузка.Но это крайне неочевидно.
Поэтому первая перегрузка превратилась в
Inject<T>(this IFixture fixture, T item)
.У метода
Freeze
несколько другая история.В прошлом оказалось, что достаточно часто встречается следующее использование AutoFixture в императивном стиле:
var foo = fixture.Create<Foo>();
fixture.Inject(foo);
Тогда, автор библиотеки решил, что это так называемая концепция заморозки.В итоге два вызова превратились в один метод
Freeze
.#полезное #tips
Какую самую главную ошибку совершают новички в ООП?
Начав изучать ООП, многие знакомятся с его тремя столпами: инкапсуляция, наследование и полиморфизм. Но удаётся ли правильно понять все из них?
Если выполнить поиск определения инкапсуляции в Google, то можно получить примерно следующее:
«Инкапсуляция в программировании — это принцип, согласно которому внутреннее устройство сущностей нужно объединять в специальной «оболочке» и скрывать от вмешательств извне.»
А теперь вспомним, что самыми популярными объектно-ориентированными языками программирования являются Java и C#, где модификаторы доступа (
На самом же деле, корректнее привести следующее определение:
«Инкапсуляция – это объединение в рамках одной структуры функций и данных, с которыми эти функции работают.»
То есть, в случае объектно-ориентированного программирования, создав некоторый объект, мы связали его данные с определёнными методами на всё время его жизни.
А в случае, например, обычных функций мы просто описали некий алгоритм, который описывает последовательность действий никак не связанных с некоторым состоянием.
Вот простой пример, чтобы понять разницу:
Начав изучать ООП, многие знакомятся с его тремя столпами: инкапсуляция, наследование и полиморфизм. Но удаётся ли правильно понять все из них?
Если выполнить поиск определения инкапсуляции в Google, то можно получить примерно следующее:
«Инкапсуляция в программировании — это принцип, согласно которому внутреннее устройство сущностей нужно объединять в специальной «оболочке» и скрывать от вмешательств извне.»
А теперь вспомним, что самыми популярными объектно-ориентированными языками программирования являются Java и C#, где модификаторы доступа (
private
, public
, protected
, etc.) это одни из самых используемых ключевых слов. И получается, что начинающие программисты путают сокрытие с инкапсуляцией.На самом же деле, корректнее привести следующее определение:
«Инкапсуляция – это объединение в рамках одной структуры функций и данных, с которыми эти функции работают.»
То есть, в случае объектно-ориентированного программирования, создав некоторый объект, мы связали его данные с определёнными методами на всё время его жизни.
А в случае, например, обычных функций мы просто описали некий алгоритм, который описывает последовательность действий никак не связанных с некоторым состоянием.
Вот простой пример, чтобы понять разницу:
class Doubler
{
private readonly int value;
public Doubler(int value) => this.value = value;
public int Double() => this.value * 2;
}
//…
public int Double(int value) => value * 2;
#полезное #tips❤16👍2
Многоликая регистрация в стандартном контейнере
Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:
Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры
Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:
public interface IBar {}Наша задача зарегистировать экземпляр
public interface IFoo {}
public class FooBar : IFoo, IBar {}
FooBar
таким образом, чтобы при внедрении зависимостей IBar
и IFoo
использовался один и тот же объект.Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры
FooBar
:services.AddSingleton<IFoo, FooBar>();Но проблема вполне решаема, достаточно лишь зарегистрировать вначале экземпляр
services.AddSingleton<IBar, FooBar>();
FooBar
и затем ссылаться на него:services.AddSingleton<FooBar>();#полезное #tips
services.AddSingleton<IFoo>(x => x.GetRequiredService<FooBar>());
services.AddSingleton<IBar>(x => x.GetRequiredService<FooBar>());
👍31
Autofac. Именованные сервисы
Контейнер Autofac предоставляет возможность внедрять конкретные зависимости, явно указывая некоторый ключ, который соотносится с желаемой зависимостью.
Например, у нас есть сервис
Чтобы указать, что мы хотим внедрить конкретную реализацию
По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.
Пример:
Контейнер Autofac предоставляет возможность внедрять конкретные зависимости, явно указывая некоторый ключ, который соотносится с желаемой зависимостью.
Например, у нас есть сервис
IDisplay
, отображающий какие-то произведения искусства IArtwork
.Чтобы указать, что мы хотим внедрить конкретную реализацию
MyPainting
, можно использовать атрибут KeyFilterAttribute
.По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.
Пример:
public class ArtDisplay : IDisplay#полезное #tips
{
public ArtDisplay([KeyFilter("MyPainting")] IArtwork art) { ... }
}
// ...
var builder = new ContainerBuilder();
builder.RegisterType<MyPainting>().Keyed<IArtwork>("MyPainting");
builder.RegisterType<ArtDisplay>().As<IDisplay>().WithAttributeFiltering();
// ...
var container = builder.Build();
❤4👍2🤔2❤🔥1
Знали ли вы...
Что в .NET 7 при разработке API больше не требуется явно указывать атрибут
Теперь биндинг параметров у действий в контроллерах также отслеживает то, что приходит из DI контейнера.
Соответственно, такой код спокойно отработает без ошибок:
#полезное #tips
Что в .NET 7 при разработке API больше не требуется явно указывать атрибут
[FromServices]
для зависимостей, указанных в параметре метода?Теперь биндинг параметров у действий в контроллерах также отслеживает то, что приходит из DI контейнера.
Соответственно, такой код спокойно отработает без ошибок:
Services.AddScoped<SomeCustomType>();Ну а в случае, когда такое неявное поведение не нравится или не требуется, его можно отключить следующим образом:
[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
public ActionResult Get(SomeCustomType service) => Ok();
}
Services.Configure<ApiBehaviorOptions>(options =>Поделитесь в комментариях темами, которые хотелось бы разобрать на канале.
{
options.DisableImplicitFromServicesParameters = true;
})
#полезное #tips
👍13😨2