День 1788. #Карьера
12 Качеств Сеньора, Которыми Должны Овладеть Джуны. Окончание
Начало 1-4
Продолжение 5-9
10. Совершенство как привычка
Кажется, Кент Бек произнес знаменитую фразу «Я не великий программист, я просто хороший программист с отличными привычками». Привычки важны, потому что они выдерживают испытание временем. Любые ваши знания о фреймворке рано или поздно устареют. Технологии меняются. Но ваши привычки останутся с вами навсегда.
Люди — существа привычек. Нелегко установить в голове новый распорядок дня. Но как только мы повторим его много раз, это войдёт в привычку, и будет легко доводить дело до конца. Лучший способ воспользоваться этим — поставить любую цель и разбить её на ежедневные действия. Затем превратите эти действия в привычки посредством повторения.
Если вы хотите стать лучше в тестировании, пишите по одному тесту каждый день. Если вы хотите быть более готовым к ежедневным митингам и ясно мыслить, ведите дневник. Найти новую работу и нужно разослать 100 резюме, отправляйте по 10 каждый день.
Выработайте новые привычки, и ваш прогресс будет постоянным и устойчивым.
11. Защита самого важного актива
Сеньор думает на перспективу не только о своём коде и технических решениях, но и о здоровье. Работа разработчиков сидячая, а это очень вредно для здоровья. Кроме того, в офисе есть всякая мусорная еда. Т.е. если вы не приложите сознательных усилий, вам будет трудно оставаться здоровым и в форме.
Но если вы хотите иметь долгую и продуктивную карьеру без проблем со спиной, диабетом или чем-то похуже, нужно начать заботиться о своём здоровье. Это не значит, ходить в спортзал 5 раз в неделю или сесть на сумасшедшую диету. Занимайтесь спортом хотя бы 2–3 раза в неделю, сократите потребление сахара и быстрых углеводов. Подумайте и о психическом здоровье. На живите по принципу «eat, sleep, code, repeat», стройте жизнь вне работы: найдите хобби, встречайтесь с друзьями, отдыхайте.
В краткосрочной перспективе вы можете прогрессировать немного медленнее. Но в долгосрочной перспективе вы сможете оставаться в игре дольше.
12. Замкните круг
Старшие разработчики понимают, как работает карма. Они знают, что как бы усердно они ни работали или насколько бы умны они ни были, десятки людей помогли им на пути к вершине. Это не умаляет их заслуг, но признаёт вклад других людей. Так они замыкают круг - помогают другим разработчикам. И делают это без обязательств. Не потому, что это может принести повышение, уважение или статус, а потому что это правильно.
Вы также можете найти время, чтобы помочь новичку. Помощь тому, кто только начинает, частично вернёт вам вдохновение. Страсть и любопытство, которые испытывает новичок, когда видит что-то впервые, очень заразительны. Ещё одна вещь, которую вы можете превратить в привычку.
Я ничего не упустил? Какие черты характера сеньоров, по-вашему, следует добавить в список? Пишите в комментарии.
Источник: https://dev.to/dragosnedelcu/12-senior-developer-traits-junior-developers-need-to-master-285m
12 Качеств Сеньора, Которыми Должны Овладеть Джуны. Окончание
Начало 1-4
Продолжение 5-9
10. Совершенство как привычка
Кажется, Кент Бек произнес знаменитую фразу «Я не великий программист, я просто хороший программист с отличными привычками». Привычки важны, потому что они выдерживают испытание временем. Любые ваши знания о фреймворке рано или поздно устареют. Технологии меняются. Но ваши привычки останутся с вами навсегда.
Люди — существа привычек. Нелегко установить в голове новый распорядок дня. Но как только мы повторим его много раз, это войдёт в привычку, и будет легко доводить дело до конца. Лучший способ воспользоваться этим — поставить любую цель и разбить её на ежедневные действия. Затем превратите эти действия в привычки посредством повторения.
Если вы хотите стать лучше в тестировании, пишите по одному тесту каждый день. Если вы хотите быть более готовым к ежедневным митингам и ясно мыслить, ведите дневник. Найти новую работу и нужно разослать 100 резюме, отправляйте по 10 каждый день.
Выработайте новые привычки, и ваш прогресс будет постоянным и устойчивым.
11. Защита самого важного актива
Сеньор думает на перспективу не только о своём коде и технических решениях, но и о здоровье. Работа разработчиков сидячая, а это очень вредно для здоровья. Кроме того, в офисе есть всякая мусорная еда. Т.е. если вы не приложите сознательных усилий, вам будет трудно оставаться здоровым и в форме.
Но если вы хотите иметь долгую и продуктивную карьеру без проблем со спиной, диабетом или чем-то похуже, нужно начать заботиться о своём здоровье. Это не значит, ходить в спортзал 5 раз в неделю или сесть на сумасшедшую диету. Занимайтесь спортом хотя бы 2–3 раза в неделю, сократите потребление сахара и быстрых углеводов. Подумайте и о психическом здоровье. На живите по принципу «eat, sleep, code, repeat», стройте жизнь вне работы: найдите хобби, встречайтесь с друзьями, отдыхайте.
В краткосрочной перспективе вы можете прогрессировать немного медленнее. Но в долгосрочной перспективе вы сможете оставаться в игре дольше.
12. Замкните круг
Старшие разработчики понимают, как работает карма. Они знают, что как бы усердно они ни работали или насколько бы умны они ни были, десятки людей помогли им на пути к вершине. Это не умаляет их заслуг, но признаёт вклад других людей. Так они замыкают круг - помогают другим разработчикам. И делают это без обязательств. Не потому, что это может принести повышение, уважение или статус, а потому что это правильно.
Вы также можете найти время, чтобы помочь новичку. Помощь тому, кто только начинает, частично вернёт вам вдохновение. Страсть и любопытство, которые испытывает новичок, когда видит что-то впервые, очень заразительны. Ещё одна вещь, которую вы можете превратить в привычку.
Я ничего не упустил? Какие черты характера сеньоров, по-вашему, следует добавить в список? Пишите в комментарии.
Источник: https://dev.to/dragosnedelcu/12-senior-developer-traits-junior-developers-need-to-master-285m
👍21
День 1789. #ЧтоНовенького
Новые Аннотации Данных в .NET 8
В .NET 8 появилось несколько дополнений к аннотациям данных, которые обеспечивают большую гибкость при проверке модели в ASP.NET Core.
1. Указание допустимых значений
Атрибут AllowedValues позволяет добавлять к свойству допустимые значения. Значения представляют собой тип object, что означает, что они могут принимать тип свойства. Т.е. можно использовать строки, числа или даже перечисления. В следующем примере для проверки свойства Fruit в классе ValidatedModel должно быть установлено значение банан или яблоко.
2. Запрет определённых значений
Атрибут DeniedValues является противоположностью атрибуту AllowedValues и запрещает определённые значения свойства. Он будет работать с типом, установленным в свойстве. В примере ниже свойство Age не может иметь значение 16 или 17:
3. Проверка строки в кодировке Base64
Атрибут Base64String гарантирует, что строка должна быть закодирована в Base64. Это гарантирует, что свойство пройдёт проверку только в том случае, если значение закодировано. В примере ниже свойство Hash должно быть закодировано в Base64:
4. Указание длины списка
Если у нас есть свойство типа списка, и мы хотим проверять минимальное и максимальное количество элементов, мы можем использовать атрибут Length. В этом примере длина списка любимых фруктов должна быть от 1 до 3 элементов:
5. Границы диапазонов
Атрибут Range существует уже некоторое время, но в него были добавлены дополнительные необязательные параметры. Если для параметра MinimIsExclusive или MaximumIsExclusive установлено значение true, минимальное или максимальное значение будет исключено из диапазона допустимых значений. В примере ниже задан диапазон от 10 до 19, не включая границы:
Источник: https://www.roundthecode.com/dotnet-tutorials/data-annotations-awesome-additions-dotnet-8
Новые Аннотации Данных в .NET 8
В .NET 8 появилось несколько дополнений к аннотациям данных, которые обеспечивают большую гибкость при проверке модели в ASP.NET Core.
1. Указание допустимых значений
Атрибут AllowedValues позволяет добавлять к свойству допустимые значения. Значения представляют собой тип object, что означает, что они могут принимать тип свойства. Т.е. можно использовать строки, числа или даже перечисления. В следующем примере для проверки свойства Fruit в классе ValidatedModel должно быть установлено значение банан или яблоко.
public class ValidatedModel
{
[AllowedValues("banana", "apple")]
public string Fruit { get; set; }
}
2. Запрет определённых значений
Атрибут DeniedValues является противоположностью атрибуту AllowedValues и запрещает определённые значения свойства. Он будет работать с типом, установленным в свойстве. В примере ниже свойство Age не может иметь значение 16 или 17:
public class ValidatedModel
{
[DeniedValues(16, 17)]
public int Age { get; set; }
}
3. Проверка строки в кодировке Base64
Атрибут Base64String гарантирует, что строка должна быть закодирована в Base64. Это гарантирует, что свойство пройдёт проверку только в том случае, если значение закодировано. В примере ниже свойство Hash должно быть закодировано в Base64:
public class ValidatedModel
{
[Base64String]
public string Hash { get; set; }
}
4. Указание длины списка
Если у нас есть свойство типа списка, и мы хотим проверять минимальное и максимальное количество элементов, мы можем использовать атрибут Length. В этом примере длина списка любимых фруктов должна быть от 1 до 3 элементов:
public class ValidatedModel
{
[Length(1, 3)]
public List<string> FavouriteFruits { get; set; }
}
5. Границы диапазонов
Атрибут Range существует уже некоторое время, но в него были добавлены дополнительные необязательные параметры. Если для параметра MinimIsExclusive или MaximumIsExclusive установлено значение true, минимальное или максимальное значение будет исключено из диапазона допустимых значений. В примере ниже задан диапазон от 10 до 19, не включая границы:
public class MyValidationModel
{
[Range(10, 19, MinimumIsExclusive = true, MaximumIsExclusive = true)]
public int Age { get; set; }
}
Источник: https://www.roundthecode.com/dotnet-tutorials/data-annotations-awesome-additions-dotnet-8
👍32
День 1790. #ЗаметкиНаПолях
Перехват HTTP-запросов с помощью DelegatingHandler
Перехват HTTP-запросов позволяет вам выполнить некоторую логику до того, как будет сделан запрос или после получения ответа. В ASP.NET перехватчик можно реализовать с помощью DelegatingHandler.
Вариант использования перехватчика — добавление заголовка (например, авторизации). Если это статическое значение, проще установить его при настройке HttpClient, но, если оно динамическое – это не подойдёт. Другим вариантом использования может быть ведение журнала или обработка ошибок. Нам бы не хотелось засорять процесс выполнения HTTP-запросов слишком большим количеством деталей, а также лучше не повторяться, если одна логика нужна в разных местах.
DelegatingHandler решает эту проблему, предоставляя единую точку, где может существовать эта логика. Общее поведение может быть написано один раз и автоматически применяется ко всем HTTP-запросам (для данного HTTP-клиента). Это упрощает тесты и изменение поведения в будущем.
Заметьте, что в примере использован первичный конструктор, добавленный в C# 12.
Чтобы использовать перехватчик, добавьте его в контейнер DI и зарегистрируйте для HTTP-клиента с помощью AddHttpMessageHandler:
После этих изменений запросы, вызываемые через ExternalClient, будут проходить обработчик AuthHeaderHandler.
Итого
DelegatingHandler позволяет перехватывать HTTP-запрос для выполнения кода до запуска запроса или после получения ответа. Это помогает удалить избыточный код, сохраняя логику приложения чистой и компактной. Подробнее о DelegatingHandler можно почитать в документации.
Источник: https://timdeschryver.dev/blog/intercepting-http-requests-with-a-delegatinghandler
Перехват HTTP-запросов с помощью DelegatingHandler
Перехват HTTP-запросов позволяет вам выполнить некоторую логику до того, как будет сделан запрос или после получения ответа. В ASP.NET перехватчик можно реализовать с помощью DelegatingHandler.
Вариант использования перехватчика — добавление заголовка (например, авторизации). Если это статическое значение, проще установить его при настройке HttpClient, но, если оно динамическое – это не подойдёт. Другим вариантом использования может быть ведение журнала или обработка ошибок. Нам бы не хотелось засорять процесс выполнения HTTP-запросов слишком большим количеством деталей, а также лучше не повторяться, если одна логика нужна в разных местах.
DelegatingHandler решает эту проблему, предоставляя единую точку, где может существовать эта логика. Общее поведение может быть написано один раз и автоматически применяется ко всем HTTP-запросам (для данного HTTP-клиента). Это упрощает тесты и изменение поведения в будущем.
public class AuthHeaderHandler(
IOptions<AuthOptions> opts) : DelegatingHandler
{
protected override async
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken ct)
{
var token = CreateToken();
request.Headers.Authorization =
new AuthenticationHeaderValue(
"Bearer", bearerToken);
var response = await
base.SendAsync(request, ct);
return response;
}
private string CreateToken()
{
// Получаем токен
return "…";
}
}
Заметьте, что в примере использован первичный конструктор, добавленный в C# 12.
Чтобы использовать перехватчик, добавьте его в контейнер DI и зарегистрируйте для HTTP-клиента с помощью AddHttpMessageHandler:
var builder =
WebApplication.CreateBuilder(args);
builder.Services
.AddTransient<AuthHeaderHandler>();
builder.Services
.AddHttpClient<ExternalClient>(
"external-service",
b =>
{
var url = builder.Configuration
.GetSection("ExternalService:Url").Value;
b.BaseAddress = new Uri(url);
})
.AddHttpMessageHandler<AuthHeaderHandler>();
var app = builder.Build();
app.UseHttpsRedirection();
app.Run();
После этих изменений запросы, вызываемые через ExternalClient, будут проходить обработчик AuthHeaderHandler.
Итого
DelegatingHandler позволяет перехватывать HTTP-запрос для выполнения кода до запуска запроса или после получения ответа. Это помогает удалить избыточный код, сохраняя логику приложения чистой и компактной. Подробнее о DelegatingHandler можно почитать в документации.
Источник: https://timdeschryver.dev/blog/intercepting-http-requests-with-a-delegatinghandler
👍12
День 1791. #DDD
Объекты-Значения в .NET. Начало
Объектом-значением (value object) называется объект, который представляет описательный аспект предметной области и не имеет собственной идентичности. Такие объекты создаются в программе для представления элементов проекта, о которых достаточно знать только, что они собой представляют, но не каким именно предметом они являются.
— Эрик Эванс
Объекты-значения инкапсулируют примитивные типы в предметной области и решают проблему одержимости примитивами.
Основные качества:
- неизменяемость,
- отсутствие идентичности,
- структурное равенство (два объекта-значения равны, если их значения одинаковы).
Реализация
Объект Booking ниже с примитивными значениями, представляющими адрес, а также даты начала и окончания бронирования:
Можно заменить эти примитивы объектами-значениями Address и DateRange:
1. Записи
В С# для представления объектов-значений можно использовать записи. Записи неизменяемы, реализуют структурное равенство и лаконичны за счёт использования первичных конструкторов:
Однако, при этом вы теряете возможность реализовывать инварианты (например, валидацию параметров). Также проблема инвариантов объектов-значений возникает при использовании выражения with.
2. Базовый класс
Альтернативный способ — использование базового класса ValueObject с переопределением равенства (метод GetValues). Наследники должны реализовать этот метод и определить компоненты равенства (равенство каких свойств означает равенство объектов).
Преимущество ValueObject в его явности. Понятно, какие классы домена представляют объекты-значения и какие свойства отвечают за равенство.
Реализация объекта-значения Address:
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals
Объекты-Значения в .NET. Начало
Объектом-значением (value object) называется объект, который представляет описательный аспект предметной области и не имеет собственной идентичности. Такие объекты создаются в программе для представления элементов проекта, о которых достаточно знать только, что они собой представляют, но не каким именно предметом они являются.
— Эрик Эванс
Объекты-значения инкапсулируют примитивные типы в предметной области и решают проблему одержимости примитивами.
Основные качества:
- неизменяемость,
- отсутствие идентичности,
- структурное равенство (два объекта-значения равны, если их значения одинаковы).
Реализация
Объект Booking ниже с примитивными значениями, представляющими адрес, а также даты начала и окончания бронирования:
public class Booking
{
public string Street { get; init; }
public string City { get; init; }
public string Country { get; init; }
public DateOnly StartDate { get; init; }
public DateOnly EndDate { get; init; }
}
Можно заменить эти примитивы объектами-значениями Address и DateRange:
public class Booking
{
public Address Address { get; init; }
public DateRange Period { get; init; }
}
1. Записи
В С# для представления объектов-значений можно использовать записи. Записи неизменяемы, реализуют структурное равенство и лаконичны за счёт использования первичных конструкторов:
public record Address(
string Street,
string City,
string Country
);
Однако, при этом вы теряете возможность реализовывать инварианты (например, валидацию параметров). Также проблема инвариантов объектов-значений возникает при использовании выражения with.
2. Базовый класс
Альтернативный способ — использование базового класса ValueObject с переопределением равенства (метод GetValues). Наследники должны реализовать этот метод и определить компоненты равенства (равенство каких свойств означает равенство объектов).
Преимущество ValueObject в его явности. Понятно, какие классы домена представляют объекты-значения и какие свойства отвечают за равенство.
public abstract class ValueObject
: IEquatable<ValueObject>
{
public static bool operator ==
(ValueObject? a, ValueObject? b)
{
if (a is null && b is null)
return true;
if (a is null || b is null)
return false;
return a.Equals(b);
}
public static bool operator !=
(ValueObject? a, ValueObject? b)
=> !(a == b);
public virtual bool Equals
(ValueObject? other)
=> other is not null && ValuesEqual(other);
public override bool Equals
(object? obj)
=> obj is ValueObject v
&& ValuesEqual(v);
public override int GetHashCode() =>
GetValues().Aggregate(
default(int),
(hash, value) =>
HashCode.Combine(hash, value.GetHashCode()));
protected abstract IEnumerable<object> GetValues();
private bool ValuesEqual(ValueObject v) =>
GetValues().SequenceEqual(v.GetValues());
}
Реализация объекта-значения Address:
public sealed class Address : ValueObject
{
public string Street { get; init; }
public string City { get; init; }
public string Country { get; init; }
protected override IEnumerable<object> GetValues()
{
yield return Street;
yield return City;
yield return Country;
}
}
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals
👍16
День 1792. #DDD
Объекты-Значения в .NET. Окончание
Начало
Когда использовать объекты-значения?
Для решения проблемы одержимости примитивами и инкапсуляции инвариантов домена. Инкапсуляция — важный аспект любой модели предметной области. У вас не должно быть возможности создать объект значения в недопустимом состоянии.
Объекты-значения также обеспечивают безопасность типов. Сравните:
С реализацией, в которую добавлен объекты-значения:
Второй вариант гораздо яснее выражает намерение и снижает вероятность ошибок (например, перепутанных дат).
Однако, если применить инварианты нужно только в нескольких местах кода, лучше обойтись без объектов-значений.
Сохранение объектов-значений с помощью EF Core
Объекты-значения можно сохранять, используя принадлежащие (Owned) и комплексные (Complex) типы EF Core.
Принадлежащие типы можно настроить, вызвав метод OwnsOne при настройке сущности. Это укажет EF сохранять объекты-значений в той же таблице в виде дополнительных столбцов:
Замечания:
- Принадлежащие типы имеют скрытое значение ключа.
- Нет поддержки необязательных (обнуляемых) принадлежащих типов.
- Поддерживаются принадлежащие коллекции с помощью OwnsMany.
- Разделение таблиц позволяет сохранять принадлежащие типы отдельно.
Комплексные типы
Это новая функция EF, доступная в .NET 8. Они не идентифицируются и не отслеживаются по значению ключа, поэтому лучше подходят для хранения объектов-значений. Комплексные типы должны быть частью типа сущности:
Ограничения для комплексных типов:
- Нет поддержки коллекций.
- Нет поддержки обнуляемых значений.
Итого
Объекты-значения помогают разработать богатую модель предметной области. Вы можете использовать их для решения проблемы одержимости примитивами и инкапсуляции инвариантов предметной области. Объекты-значения могут уменьшить количество ошибок, предотвращая создание недопустимых объектов домена.
Вы можете использовать записи или базовый класс ValueObject для представления объектов-значений. Это должно зависеть от конкретных требований и сложности домена.
Источник: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals
Объекты-Значения в .NET. Окончание
Начало
Когда использовать объекты-значения?
Для решения проблемы одержимости примитивами и инкапсуляции инвариантов домена. Инкапсуляция — важный аспект любой модели предметной области. У вас не должно быть возможности создать объект значения в недопустимом состоянии.
Объекты-значения также обеспечивают безопасность типов. Сравните:
public interface IPricingService
{
decimal Calculate(
Apartment apartment,
DateOnly start,
DateOnly end);
}
С реализацией, в которую добавлен объекты-значения:
public interface IPricingService
{
PricingDetails Calculate(
Apartment apartment,
DateRange period);
}
Второй вариант гораздо яснее выражает намерение и снижает вероятность ошибок (например, перепутанных дат).
Однако, если применить инварианты нужно только в нескольких местах кода, лучше обойтись без объектов-значений.
Сохранение объектов-значений с помощью EF Core
Объекты-значения можно сохранять, используя принадлежащие (Owned) и комплексные (Complex) типы EF Core.
Принадлежащие типы можно настроить, вызвав метод OwnsOne при настройке сущности. Это укажет EF сохранять объекты-значений в той же таблице в виде дополнительных столбцов:
public void Configure(
EntityTypeBuilder<Apartment> builder)
{
builder.ToTable("apartments");
builder.OwnsOne(p => p.Address);
builder.OwnsOne(p => p.Price, b =>
{
b.Property(m => m.Currency)
.HasConversion(
curr => curr.Code,
code => Currency.FromCode(code));
});
}
Замечания:
- Принадлежащие типы имеют скрытое значение ключа.
- Нет поддержки необязательных (обнуляемых) принадлежащих типов.
- Поддерживаются принадлежащие коллекции с помощью OwnsMany.
- Разделение таблиц позволяет сохранять принадлежащие типы отдельно.
Комплексные типы
Это новая функция EF, доступная в .NET 8. Они не идентифицируются и не отслеживаются по значению ключа, поэтому лучше подходят для хранения объектов-значений. Комплексные типы должны быть частью типа сущности:
public void Configure(
EntityTypeBuilder<Apartment> builder)
{
builder.ToTable("apartments");
builder.ComplexProperty(p => p.Address);
}
Ограничения для комплексных типов:
- Нет поддержки коллекций.
- Нет поддержки обнуляемых значений.
Итого
Объекты-значения помогают разработать богатую модель предметной области. Вы можете использовать их для решения проблемы одержимости примитивами и инкапсуляции инвариантов предметной области. Объекты-значения могут уменьшить количество ошибок, предотвращая создание недопустимых объектов домена.
Вы можете использовать записи или базовый класс ValueObject для представления объектов-значений. Это должно зависеть от конкретных требований и сложности домена.
Источник: https://www.milanjovanovic.tech/blog/value-objects-in-dotnet-ddd-fundamentals
👍9
День 1793. #ЗаметкиНаПолях
Препарируем Cookie в ASP.NET Core. Начало
ASP.NET Core создаёт несколько файлов cookie, включая cookie аутентификации, сессии и защиты от подделки (antiforgery). Рассмотрим, что они содержат и как защищены.
Защита cookie
Содержимое этих cookie защищено с помощью комбинации механизмов шифрования и подписи. Эти защитные меры, реализуемые через API защиты данных (DPAPI), обеспечивают конфиденциальность и целостность информации, хранящейся в них.
DPAPI — это платформа для защиты конфиденциальных данных, управления шифрованием и упрощения ротации ключей. Она предлагает унифицированный интерфейс для шифрования, дешифрования и подписи, что повышает безопасность данных приложений.
1. Cookie аутентификации
Поддерживает аутентификацию пользователя по HTTP-запросам. Когда пользователь входит в систему, система аутентификации обычно выдаёт клиенту cookie, вроде следующего:
DPAPI шифрует его, делая его содержимое недоступным и защищённым от несанкционированного доступа. К счастью, мы можем отключить эту защиту, подставив свой класс защиты данных:
Добавим этот класс защиты в настройки cookie аутентификации:
Теперь выдаваемые системой cookie вообще не будут защищены. При входе в систему будет создан cookie аутентификации со всеми утверждениями (claims), которые были заданы в объекте ClaimsPrincipal:
Содержимое cookie - просто текст, закодированный в Base64. Если выполнить декодирование (любым инструментом Base64Decode), мы увидим что-нибудь вроде следующего:
*здесь нечитаемые символы были заменены точками.
То есть, внутри cookie размещён весь тикет аутентификации (состоящий из ClaimsPrincipal и AuthenticationProperties).
Окончание следует…
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
Препарируем Cookie в ASP.NET Core. Начало
ASP.NET Core создаёт несколько файлов cookie, включая cookie аутентификации, сессии и защиты от подделки (antiforgery). Рассмотрим, что они содержат и как защищены.
Защита cookie
Содержимое этих cookie защищено с помощью комбинации механизмов шифрования и подписи. Эти защитные меры, реализуемые через API защиты данных (DPAPI), обеспечивают конфиденциальность и целостность информации, хранящейся в них.
DPAPI — это платформа для защиты конфиденциальных данных, управления шифрованием и упрощения ротации ключей. Она предлагает унифицированный интерфейс для шифрования, дешифрования и подписи, что повышает безопасность данных приложений.
1. Cookie аутентификации
Поддерживает аутентификацию пользователя по HTTP-запросам. Когда пользователь входит в систему, система аутентификации обычно выдаёт клиенту cookie, вроде следующего:
Set-Cookie: .AspNetCore.cookie=CfDJ8MSO_2XEvwalH…;
expires=Thu, 11 Jan 2024 07:38:16 GMT; path=/; secure; samesite=lax; httponly
DPAPI шифрует его, делая его содержимое недоступным и защищённым от несанкционированного доступа. К счастью, мы можем отключить эту защиту, подставив свой класс защиты данных:
public class MyProtector : IDataProtector
{
public IDataProtector
CreateProtector(string purpose)
{
return new MyProtector();
}
public byte[] Protect(byte[] text)
{
return text;
}
public byte[] Unprotect(byte[] data)
{
return data;
}
}
Добавим этот класс защиты в настройки cookie аутентификации:
builder.Services.AddAuthentication("cookie")
.AddCookie("cookie", o =>
{
o.DataProtectionProvider = new MyProtector();
});
Теперь выдаваемые системой cookie вообще не будут защищены. При входе в систему будет создан cookie аутентификации со всеми утверждениями (claims), которые были заданы в объекте ClaimsPrincipal:
Set-Cookie:
.AspNetCore.cookie=BQAAAAZjb29ra…01U;
expires= Thu, 11 Jan 2024 07:57:04 GMT; path=/; secure; samesite=lax; httponly
Содержимое cookie - просто текст, закодированный в Base64. Если выполнить декодирование (любым инструментом Base64Decode), мы увидим что-нибудь вроде следующего:
......cookie.....cookie...........Jon Smith..........
=https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.email@server.com...........JobTitle.Developer...........persistent...issued.Thu, 28 Dec 2023 07:57:04 GMT..expires.Thu, 11 Jan 2024 07:57:04 GMT
*здесь нечитаемые символы были заменены точками.
То есть, внутри cookie размещён весь тикет аутентификации (состоящий из ClaimsPrincipal и AuthenticationProperties).
Окончание следует…
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
👍16
День 1794. #ЗаметкиНаПолях
Препарируем Cookie в ASP.NET Core. Окончание
Начало
2. Cookie сессии
Сервис сессий в ASP.NET Core — это механизм управления пользовательскими данными между HTTP-запросами, который часто используется в таких сценариях, как обслуживание корзины покупок. Этот сервис отправляет браузеру защищённый файл cookie для отслеживания данных. Важно знать, что этот cookie и его назначение не связаны с cookie аутентификации.
Приложение может попросить сервис сессий запомнить временные данные текущего пользователя. Чтобы использовать сервис сессий, зарегистрируйте его в конвейере запросов:
Cookie сессии выглядит примерно так:
Этот cookie также защищён с помощью API защиты данных; однако для него нет возможности предоставить собственный класс защиты, как мы это сделали в предыдущем примере.
Один из вариантов посмотреть на содержимое — загрузить и изменить код промежуточного ПО сессий. Хотя, особой необходимости в этом нет. Cookie сессии хранит простой GUID - ключ, используемый для поиска объекта сессии для текущего запроса. Место хранения данных зависит от того, как вы настроили систему сессий. Система сессий в ASP.NET Core предоставляет различные варианты хранения, включая хранилище в памяти, распределённый кэш и внешних поставщиков хранилищ, что позволяет разработчикам выбирать наиболее подходящий метод для потребностей масштабируемости и производительности приложения.
3. Cookie защиты от подделки
Этот cookie предназначен для защиты от атак с подделкой межсайтовых запросов (CSRF). Он работает в системе на основе токенов, где токен хранится в файле cookie, а также встраивается в HTML-формы. При отправке формы токен из данных формы должен соответствовать токену в файле cookie для обеспечения безопасности.
DPAPI также защищает этот cookie. Реализацией этой защиты занимается класс DefaultAntiforgeryTokenSerializer, подробно изучить который можно здесь.
Вот пример такого cookie:
Не будем здесь углубляться в особенности внутренней структуры токена, это можно посмотреть в коде класса сериализатора по ссылке выше.
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
Препарируем Cookie в ASP.NET Core. Окончание
Начало
2. Cookie сессии
Сервис сессий в ASP.NET Core — это механизм управления пользовательскими данными между HTTP-запросами, который часто используется в таких сценариях, как обслуживание корзины покупок. Этот сервис отправляет браузеру защищённый файл cookie для отслеживания данных. Важно знать, что этот cookie и его назначение не связаны с cookie аутентификации.
Приложение может попросить сервис сессий запомнить временные данные текущего пользователя. Чтобы использовать сервис сессий, зарегистрируйте его в конвейере запросов:
// регистрация сервиса
builder.Services.AddSession(options =>
{
//… параметры …
});
…
//использование сервиса
app.UseSession();
Cookie сессии выглядит примерно так:
Set-Cookie:
.AspNetCore.Session=CfDJ8MSO%2F2XEvalHlF%2Fgv69RLqD6mFWeTVC1MG3dYxNWftq625VyGyqF%2BeIq2
xaqgm1cd4McTp0ydSLcRYraIA4%2F%2Bn89FKFhz567AcC%2FeSnwabg4eRrlFAeWLFBq0K8zl2ISdMPcY0pj%
2BtAJQgC5NIte76QR4TlheM1ZhsD98WAdAvKM; path=/; samesite=lax; httponly
Этот cookie также защищён с помощью API защиты данных; однако для него нет возможности предоставить собственный класс защиты, как мы это сделали в предыдущем примере.
Один из вариантов посмотреть на содержимое — загрузить и изменить код промежуточного ПО сессий. Хотя, особой необходимости в этом нет. Cookie сессии хранит простой GUID - ключ, используемый для поиска объекта сессии для текущего запроса. Место хранения данных зависит от того, как вы настроили систему сессий. Система сессий в ASP.NET Core предоставляет различные варианты хранения, включая хранилище в памяти, распределённый кэш и внешних поставщиков хранилищ, что позволяет разработчикам выбирать наиболее подходящий метод для потребностей масштабируемости и производительности приложения.
3. Cookie защиты от подделки
Этот cookie предназначен для защиты от атак с подделкой межсайтовых запросов (CSRF). Он работает в системе на основе токенов, где токен хранится в файле cookie, а также встраивается в HTML-формы. При отправке формы токен из данных формы должен соответствовать токену в файле cookie для обеспечения безопасности.
DPAPI также защищает этот cookie. Реализацией этой защиты занимается класс DefaultAntiforgeryTokenSerializer, подробно изучить который можно здесь.
Вот пример такого cookie:
Set-Cookie: .AspNetCore.Antiforgery.YCD23e8AirM=CfDJ8MSO_2XEvalHlF_gv69RLqDarftldkzWvbxvac
LnZ4WtaTDcgSCPmxQdmK8OHbGfMIUvV0VhcFkx4Ys8jPppklGBUGWRTsXcwxhMBl7nqZA0s_xYN5jJq3J7
LrH8EikY82bOYmSoA_wEYCxkcrBEEAs; path=/; samesite=strict; httponly
Не будем здесь углубляться в особенности внутренней структуры токена, это можно посмотреть в коде класса сериализатора по ссылке выше.
Источник: https://nestenius.se/2023/11/22/exploring-what-is-inside-the-asp-net-core-cookies/
👍9
День 1795. #ЗаметкиНаПолях
Изоляция Данных в Модульном Монолите
Модульный монолит — это архитектурный подход, который становится очень популярным. Он пытается побороть недостатки монолитной и микросервисной архитектур. Одна из проблем монолитной архитектуры, — тесная связь между компонентами и зависимости между различными частями системы. Модульные монолиты решают эту проблему, чётко определяя границы модулей и шаблоны взаимодействия. Но один аспект, который нельзя упустить из виду, — это изоляция данных между модулями, что гарантирует, независимость и слабую связанность модулей.
Изоляция модулей облегчает внесение новых изменений в систему. Когда компоненты слабо связаны, возникает меньше непреднамеренных побочных эффектов.
Модульный монолит имеет строгие правила целостности данных:
- Каждый модуль может обращаться только к своим таблицам.
- Запрещено совместное использование таблиц или объектов между модулями.
- Объединения разрешены только между таблицами одного модуля.
Модули должны быть автономными. Каждый модуль обрабатывает только свои данные. Другие модули могут получить доступ к этим данным, используя общедоступный API модуля.
Уровни изоляции
1. Отдельные таблицы
Изначально у нас нет изоляции на уровне базы данных. Таблицы для всех модулей находятся внутри одной БД. Определить, какие таблицы к какому модулю принадлежат, непросто. Этот подход отлично работает до определённого размера приложения. Но чем больше у вас таблиц, тем сложнее становится изолировать их между модулями. Это можно улучшить, добавив логическую изоляцию между таблицами.
2. Раздельные схемы
Группировка связанных таблиц в БД — это способ обеспечить логическую изоляцию. Вы можете реализовать это, используя схемы. Каждый модуль имеет уникальную схему, содержащую таблицы модуля. Теперь легко отличить, какой модуль какие таблицы содержит. В приложении это легко реализовать, используя несколько контекстов базы данных в EF Core. Вы также можете ввести правила, предотвращающие запрос данных из других модулей и проверять это, например, с помощью тестов архитектуры. Это первый этап изоляции данных при создании модульного монолита.
3. Раздельные БД
Этот подход имеет больше ограничений, чем изоляция данных с помощью схем, однако это лучший вариант, если вам нужны строгие правила изоляции данных между модулями. Недостатком является большая сложность эксплуатации. Вам необходимо управлять инфраструктурой для нескольких БД. Однако это отличный шаг к извлечению модулей. Сначала вы перемещаете таблицы модуля, который хотите извлечь, в отдельную БД. Это также заставит вас решать любые проблемы связанности между вашими модулями. Если все таблицы модуля извлечены в отдельную БД, значит он готов (при необходимости) существовать независимо в виде микросервиса.
4. Различные хранилища
Не обязательно использовать один и тот же тип БД для всех модулей. Реляционные БД решают широкий спектр проблем. Но иногда БД документов или графов оказывается гораздо лучшим решением. Идея здесь аналогичная: вы выполняете изоляцию данных, используя отдельные БД, но используете другой тип БД для решения конкретных проблем. Например, реляционную базу данных для большинства модулей и БД документов для отдельного модуля, где она имеет преимущество перед реляционной. Это может быть выгодным компромиссом для вашего варианта использования. Но требует тщательного планирования.
Итого
Как минимум, стоит попробовать использовать логическую изоляцию с использованием схем. Её легко реализовать, и она помогает лучше понять границы модулей и держать связанность модулей под контролем.
Источник: https://www.milanjovanovic.tech/blog/modular-monolith-data-isolation
Изоляция Данных в Модульном Монолите
Модульный монолит — это архитектурный подход, который становится очень популярным. Он пытается побороть недостатки монолитной и микросервисной архитектур. Одна из проблем монолитной архитектуры, — тесная связь между компонентами и зависимости между различными частями системы. Модульные монолиты решают эту проблему, чётко определяя границы модулей и шаблоны взаимодействия. Но один аспект, который нельзя упустить из виду, — это изоляция данных между модулями, что гарантирует, независимость и слабую связанность модулей.
Изоляция модулей облегчает внесение новых изменений в систему. Когда компоненты слабо связаны, возникает меньше непреднамеренных побочных эффектов.
Модульный монолит имеет строгие правила целостности данных:
- Каждый модуль может обращаться только к своим таблицам.
- Запрещено совместное использование таблиц или объектов между модулями.
- Объединения разрешены только между таблицами одного модуля.
Модули должны быть автономными. Каждый модуль обрабатывает только свои данные. Другие модули могут получить доступ к этим данным, используя общедоступный API модуля.
Уровни изоляции
1. Отдельные таблицы
Изначально у нас нет изоляции на уровне базы данных. Таблицы для всех модулей находятся внутри одной БД. Определить, какие таблицы к какому модулю принадлежат, непросто. Этот подход отлично работает до определённого размера приложения. Но чем больше у вас таблиц, тем сложнее становится изолировать их между модулями. Это можно улучшить, добавив логическую изоляцию между таблицами.
2. Раздельные схемы
Группировка связанных таблиц в БД — это способ обеспечить логическую изоляцию. Вы можете реализовать это, используя схемы. Каждый модуль имеет уникальную схему, содержащую таблицы модуля. Теперь легко отличить, какой модуль какие таблицы содержит. В приложении это легко реализовать, используя несколько контекстов базы данных в EF Core. Вы также можете ввести правила, предотвращающие запрос данных из других модулей и проверять это, например, с помощью тестов архитектуры. Это первый этап изоляции данных при создании модульного монолита.
3. Раздельные БД
Этот подход имеет больше ограничений, чем изоляция данных с помощью схем, однако это лучший вариант, если вам нужны строгие правила изоляции данных между модулями. Недостатком является большая сложность эксплуатации. Вам необходимо управлять инфраструктурой для нескольких БД. Однако это отличный шаг к извлечению модулей. Сначала вы перемещаете таблицы модуля, который хотите извлечь, в отдельную БД. Это также заставит вас решать любые проблемы связанности между вашими модулями. Если все таблицы модуля извлечены в отдельную БД, значит он готов (при необходимости) существовать независимо в виде микросервиса.
4. Различные хранилища
Не обязательно использовать один и тот же тип БД для всех модулей. Реляционные БД решают широкий спектр проблем. Но иногда БД документов или графов оказывается гораздо лучшим решением. Идея здесь аналогичная: вы выполняете изоляцию данных, используя отдельные БД, но используете другой тип БД для решения конкретных проблем. Например, реляционную базу данных для большинства модулей и БД документов для отдельного модуля, где она имеет преимущество перед реляционной. Это может быть выгодным компромиссом для вашего варианта использования. Но требует тщательного планирования.
Итого
Как минимум, стоит попробовать использовать логическую изоляцию с использованием схем. Её легко реализовать, и она помогает лучше понять границы модулей и держать связанность модулей под контролем.
Источник: https://www.milanjovanovic.tech/blog/modular-monolith-data-isolation
👍12
День 1796.
В последний день года предлагаю вам вспомнить его. Поиграем в бинго? Присылайте свои ответы, от чего страдали (или чем наслаждались, кто вас знает 😉) в уходящем году.
PS: В Windows
В последний день года предлагаю вам вспомнить его. Поиграем в бинго? Присылайте свои ответы, от чего страдали (или чем наслаждались, кто вас знает 😉) в уходящем году.
PS: В Windows
Win+Shift+S
для снимка экрана, потом в уведомлениях Snip & Sketch для рисования на нём. Вычёркиваем то, что было.👍10
День 1797.
Дорогие подписчики, с новым годом!
Желаю вам кода без багов, зелёных тестов и вообще поменьше попадать в ситуации, описанные во вчерашнем «Бинго».
А ещё у меня для вас парочка подарков. Вот такие кружки разыграю среди подписчиков через несколько дней. Всё что нужно сделать, просто оставить комментарий «хочу кружку».
Одно замечание, победители, будьте готовы предоставить ФИО и адрес. И извините, доставка только по РФ.
Дорогие подписчики, с новым годом!
Желаю вам кода без багов, зелёных тестов и вообще поменьше попадать в ситуации, описанные во вчерашнем «Бинго».
А ещё у меня для вас парочка подарков. Вот такие кружки разыграю среди подписчиков через несколько дней. Всё что нужно сделать, просто оставить комментарий «хочу кружку».
Одно замечание, победители, будьте готовы предоставить ФИО и адрес. И извините, доставка только по РФ.
👍40
День 1798.
Зашёл на подкаст Radio DotNet и подвёл с ними итоги года. Довольно интересно поговорили, не знаю, будет ли интересно слушать, поэтому обязательно послушайте и расскажите, как вам 😊
Отмечу, что со звуком у меня фиговенько, извините, писал на встроенный микрофон.
Слушать тут.
Посмотреть можно тут.
Зашёл на подкаст Radio DotNet и подвёл с ними итоги года. Довольно интересно поговорили, не знаю, будет ли интересно слушать, поэтому обязательно послушайте и расскажите, как вам 😊
Отмечу, что со звуком у меня фиговенько, извините, писал на встроенный микрофон.
Слушать тут.
Посмотреть можно тут.
85 выпуск
Новогодние посиделки 2023 — Подкаст «RadioDotNet»
Подкаст RadioDotNet выпуск №85 от 31 декабря 2023 года Представляем вашему вниманию традиционный праздничный выпуск. В этот раз не будет никаких новостей. Вместе с нашими слушателями мы обсудим всякое. Всех с Новым Годом! Сайт подкаста: radio.dotn
👍17
День 1799. #TipsAndTricks
10 Крутых Трюков в C#. Начало
Предлагаю вам подборку крутых и малоизвестных трюков, которые помогут вам улучшить ваш код и повысить его эффективность. Начнём с простого.
1. Использование атрибутов информации о вызывающем объекте
Эти атрибуты позволяют получить информацию об объекте, вызывающем метод, что может быть полезно для отладки и логирования:
В примере выше метод Log() использует атрибуты информации о вызывающем объекте для автоматического включения имени члена вызывающего объекта, пути к файлу и номера строки в сообщение лога. Это упрощает отслеживание источников в логах без необходимости вручную включать эту информацию.
Пример вывода при вызове из Program.cs:
2. Эффективные по памяти коллекции с помощью Span<T>
Span<T> — позволяет работать с непрерывными блоками памяти без создания новых массивов или ненужного выделения памяти в куче. Его можно использовать для создания «срезов» существующих массивов или других сегментов памяти без копирования данных.
Как это работает и почему это полезно:
В приведенном выше примере метод ProcessArray обрабатывает большой массив байтов, разделяя его на более мелкие части. Вместо создания новых массивов для каждого фрагмента, что привело бы к увеличению использования памяти и накладным расходам на сбор мусора, метод использует Span<byte> для создания «представления» исходного массива для каждой части. Метод расширения AsSpan используется для создания Span<byte> из исходного массива, начиная с текущего индекса (i) и имеющего желаемую длину (len). Полученный Span<byte> можно использовать как массив, но это не приводит к дополнительному выделению памяти. Метод ProcessChunk демонстрирует, как можно работать со Span<byte> для манипуляций с данными, на которые он указывает. В этом случае значение каждого байта удваивается.
Использование Span<T> может помочь написать более эффективно использующий память и производительный код за счёт уменьшения количества аллокаций в куче и ненужного копирования данных, особенно при работе с большими наборами данных или в сценариях, критичных к производительности.
См. подробнее «Используем Span Для Производительности»
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Начало
Предлагаю вам подборку крутых и малоизвестных трюков, которые помогут вам улучшить ваш код и повысить его эффективность. Начнём с простого.
1. Использование атрибутов информации о вызывающем объекте
Эти атрибуты позволяют получить информацию об объекте, вызывающем метод, что может быть полезно для отладки и логирования:
public static void Log(
string message,
[CallerMemberName] string member = "",
[CallerFilePath] string path = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine(
$"[{member} ({line}) в {path}]: {message}");
}
// вызов
Log("Что-то пошло не так.");
В примере выше метод Log() использует атрибуты информации о вызывающем объекте для автоматического включения имени члена вызывающего объекта, пути к файлу и номера строки в сообщение лога. Это упрощает отслеживание источников в логах без необходимости вручную включать эту информацию.
Пример вывода при вызове из Program.cs:
[<Main>$ (7) в D:\source\Program.cs]: Что-то пошло не так.
2. Эффективные по памяти коллекции с помощью Span<T>
Span<T> — позволяет работать с непрерывными блоками памяти без создания новых массивов или ненужного выделения памяти в куче. Его можно использовать для создания «срезов» существующих массивов или других сегментов памяти без копирования данных.
public static void ProcessArray (byte[] arr)
{
const int size = 1024;
for (int i=0; i<arr.Length; i+=size)
{
int len = Math.Min(size, arr.Length - i);
var chunk = arr.AsSpan(i, len);
ProcessChunk(chunk);
}
}
public static void ProcessChunk(Span<byte> chunk)
{
// Обработка части без создания нового массива
for(int i=0; i<chunk.Length; i++)
chunk[i] *= 2;
}
Как это работает и почему это полезно:
В приведенном выше примере метод ProcessArray обрабатывает большой массив байтов, разделяя его на более мелкие части. Вместо создания новых массивов для каждого фрагмента, что привело бы к увеличению использования памяти и накладным расходам на сбор мусора, метод использует Span<byte> для создания «представления» исходного массива для каждой части. Метод расширения AsSpan используется для создания Span<byte> из исходного массива, начиная с текущего индекса (i) и имеющего желаемую длину (len). Полученный Span<byte> можно использовать как массив, но это не приводит к дополнительному выделению памяти. Метод ProcessChunk демонстрирует, как можно работать со Span<byte> для манипуляций с данными, на которые он указывает. В этом случае значение каждого байта удваивается.
Использование Span<T> может помочь написать более эффективно использующий память и производительный код за счёт уменьшения количества аллокаций в куче и ненужного копирования данных, особенно при работе с большими наборами данных или в сценариях, критичных к производительности.
См. подробнее «Используем Span Для Производительности»
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍33
День 1800. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
Начало
3. Использование оператора отказа
Оператор отказа (discard) используется для игнорирования значений, которые не нужны в определённом контексте, что делает код более кратким и простым для понимания.
В примере выше отказ (
Оператор отказа можно использовать в out-параметрах или в сопоставлении по шаблону, когда вам не нужно само значение:
4. Условная слабая таблица для привязки метаданных
Условная слабая таблица (Conditional Weak Table) позволяет связывать метаданные с объектами без изменения их исходной структуры. Она использует слабые ссылки, поэтому не мешает сборщику мусора утилизировать объекты, когда они больше не используются.
Как это работает и почему это полезно:
В примере выше запись Person не имеет метаданных. Статический класс PersonMetadata использует ConditionalWeakTable, чтобы связать метаданные с экземплярами Person без изменения исходного типа. Этот подход полезен, когда вы хотите хранить дополнительную информацию для объектов, не изменяя их структуру и не создавая строгие ссылки, которые могут помешать сборке мусора.
Методы Set и Get в классе PersonMetadata позволяют хранить и извлекать метаданные для экземпляров Person. Метаданные хранятся в словаре, который затем связывается с объектом с помощью ConditionalWeakTable. Таблица привязана слабой ссылкой, поэтому, когда объект больше не используется и подлежит сборке мусора, связанные метаданные также будут собраны.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
Начало
3. Использование оператора отказа
Оператор отказа (discard) используется для игнорирования значений, которые не нужны в определённом контексте, что делает код более кратким и простым для понимания.
(int min, _) = GetMinMax(numbers);
Console.WriteLine($"Minimum: {min}");
В примере выше отказ (
_
) используется, чтобы игнорировать значение max, возвращаемое методом GetMinMax(). Это ясно обозначает, что здесь нам не нужно максимальное значение.Оператор отказа можно использовать в out-параметрах или в сопоставлении по шаблону, когда вам не нужно само значение:
if(int.TryParse("123", out _))
Console.WriteLine("Это целое число.");
if (shape is Circle _)
Console.WriteLine("Фигура - круг");
4. Условная слабая таблица для привязки метаданных
Условная слабая таблица (Conditional Weak Table) позволяет связывать метаданные с объектами без изменения их исходной структуры. Она использует слабые ссылки, поэтому не мешает сборщику мусора утилизировать объекты, когда они больше не используются.
public record Person(string Name);
public static class PersonMetadata
{
private static readonly
ConditionalWeakTable<Person, Dictionary<string, object>>
Metadata = [];
public static void Set(
Person person,
string key,
object value)
{
var metadata = Metadata.GetOrCreateValue(person);
metadata[key] = value;
}
public static object? Get(Person person, string key)
{
if (
Metadata.TryGetValue(person, out var data)
&& data.TryGetValue(key, out var value))
{
return value;
}
return null;
}
}
// использование
var person = new Person("Jon Smith");
PersonMetadata.Set(person, "age", 42);
Console.WriteLine(PersonMetadata.Get(person, "age"));
Как это работает и почему это полезно:
В примере выше запись Person не имеет метаданных. Статический класс PersonMetadata использует ConditionalWeakTable, чтобы связать метаданные с экземплярами Person без изменения исходного типа. Этот подход полезен, когда вы хотите хранить дополнительную информацию для объектов, не изменяя их структуру и не создавая строгие ссылки, которые могут помешать сборке мусора.
Методы Set и Get в классе PersonMetadata позволяют хранить и извлекать метаданные для экземпляров Person. Метаданные хранятся в словаре, который затем связывается с объектом с помощью ConditionalWeakTable. Таблица привязана слабой ссылкой, поэтому, когда объект больше не используется и подлежит сборке мусора, связанные метаданные также будут собраны.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍36
День 1801. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5. Конвейеры для потоковой обработки
Pipelines — библиотека потоковой обработки в .NET для эффективной обработки больших потоков данных с низкими задержками. Пространство имён System.IO.Pipelines предоставляет абстракции для чтения и записи данных в потоковом режиме, минимизируя при этом выделение памяти и копирование.
Вывод:
Как это работает и почему это полезно:
В примере выше метод ProcessStream создаёт новый экземпляр Pipe и запускает две задачи: для записи данных в канал (FillPipe) и для чтения данных из канала (ReadPipe). Task.WhenAll используется для ожидания завершения обеих задач. FillPipe считывает данные из входного потока и записывает их в PipeWriter. Он делает это в цикле, получая память от PipeWriter и считывая данные из потока в память. Метод PipeWriter.FlushAsync вызывается, чтобы сигнализировать о том, что данные доступны для чтения, и цикл продолжается до тех пор, пока поток не будет исчерпан или канал не будет закрыт. Метод ReadPipeAsync ожидает PipeReader.ReadAsync, который возвращает ReadResult, содержащий буфер ReadOnlySequence<byte>. Буфер обрабатывается частями, а метод PipeReader.AdvanceTo вызывается, чтобы сигнализировать о том, что данные были использованы. Цикл продолжается до тех пор, пока конвейер не будет завершён.
Использование конвейеров для потоковой обработки может обеспечить значительный выигрыш в производительности, особенно в сценариях, где требуется обработка с малой задержкой и минимальное выделение памяти. Абстракции и дизайн библиотеки упрощают обработку сложных сценариев потоковой передачи, таких как сетевое взаимодействие или файловый ввод-вывод, с эффективным управлением ресурсами и оптимальной производительностью.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5. Конвейеры для потоковой обработки
Pipelines — библиотека потоковой обработки в .NET для эффективной обработки больших потоков данных с низкими задержками. Пространство имён System.IO.Pipelines предоставляет абстракции для чтения и записи данных в потоковом режиме, минимизируя при этом выделение памяти и копирование.
async Task ProcessStream(
Stream stream)
{
var pipe = new Pipe();
var write = FillPipe(stream, pipe.Writer);
var read = ReadPipe(pipe.Reader);
await Task.WhenAll(write, read);
}
async Task FillPipe(
Stream stream,
PipeWriter writer
)
{
const int minSize = 512;
while (true)
{
var memory = writer.GetMemory(minSize);
int bytes = await stream.ReadAsync(memory);
if (bytes == 0)
break;
writer.Advance(bytes);
var result = await writer.FlushAsync();
if (result.IsCompleted)
break;
}
writer.Complete();
}
async Task ReadPipe(
PipeReader reader)
{
while (true)
{
var result = await reader.ReadAsync();
var buffer = result.Buffer;
foreach (var seg in buffer)
{
// Обработка данных частями
Console.WriteLine(
$"Прочитано {seg.Length} байт");
}
reader.AdvanceTo(buffer.End);
if (result.IsCompleted)
break;
}
reader.Complete();
}
// использование
using var stream =
new MemoryStream(
Encoding.UTF8.GetBytes("Hello, Pipelines!"));
await ProcessStream(stream);
Вывод:
Прочитано 17 байт
Как это работает и почему это полезно:
В примере выше метод ProcessStream создаёт новый экземпляр Pipe и запускает две задачи: для записи данных в канал (FillPipe) и для чтения данных из канала (ReadPipe). Task.WhenAll используется для ожидания завершения обеих задач. FillPipe считывает данные из входного потока и записывает их в PipeWriter. Он делает это в цикле, получая память от PipeWriter и считывая данные из потока в память. Метод PipeWriter.FlushAsync вызывается, чтобы сигнализировать о том, что данные доступны для чтения, и цикл продолжается до тех пор, пока поток не будет исчерпан или канал не будет закрыт. Метод ReadPipeAsync ожидает PipeReader.ReadAsync, который возвращает ReadResult, содержащий буфер ReadOnlySequence<byte>. Буфер обрабатывается частями, а метод PipeReader.AdvanceTo вызывается, чтобы сигнализировать о том, что данные были использованы. Цикл продолжается до тех пор, пока конвейер не будет завершён.
Использование конвейеров для потоковой обработки может обеспечить значительный выигрыш в производительности, особенно в сценариях, где требуется обработка с малой задержкой и минимальное выделение памяти. Абстракции и дизайн библиотеки упрощают обработку сложных сценариев потоковой передачи, таких как сетевое взаимодействие или файловый ввод-вывод, с эффективным управлением ресурсами и оптимальной производительностью.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍25👎1
День 1802. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Вывод:
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Func<T, object> GeneratePropertyGetter<T>(
string property
)
{
var param =
Expression.Parameter(typeof(T), "obj");
var prop =
Expression.Property(param, property);
var conv =
Expression.Convert(prop, typeof(object));
var lambda =
Expression.Lambda<Func<T, object>>(conv, param);
return lambda.Compile();
}
// использование
var person = new Person("John Doe", 30);
var getName = GeneratePropertyGetter<Person>("Name");
var getAge = GeneratePropertyGetter<Person>("Age");
Console.WriteLine(
$"Name: {getName(person)}, Age: {getAge(person)}");
record Person(string Name, int Age);
Вывод:
Name: John Doe, Age: 30
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍11
День 1803. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
async Task ProcessData()
{
var channel = Channel.CreateUnbounded<int>();
var producer = Task.Run(async () =>
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"Произведено: {i}");
await channel.Writer.WriteAsync(i);
await Task.Delay(1000);
}
channel.Writer.Complete();
});
var consumer = Task.Run(async () =>
{
await foreach (
var i in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Обработано: {i}");
}
});
await Task.WhenAll(producer, consumer);
}
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍31
День 1804. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
Вывод:
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
async Task ExecuteDynamicCodeAsync(
string code)
{
string sourceCode = $@"
using System;
namespace DynamicCode;
public class Runner
{{
public static void Run()
{{
{code}
}}
}}
";
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Private.CoreLib.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Console.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Runtime.dll"))
};
var compilation = CSharpCompilation.Create("DynamicCode")
.WithOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary))
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
Console.WriteLine("Компиляция не удалась");
return;
}
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
var type = assembly.GetType("DynamicCode.Runner");
var method = type.GetMethod("Run",
BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
}
// использование
await ExecuteDynamicCodeAsync(
"Console.WriteLine(\"Привет, динамический код!\");"
);
Вывод:
Привет, динамический код!
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍15
День 1805.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
👍18👎1
День 1806. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
dynamic ToDynamic(object anon)
{
var dyn = new ExpandoObject()
as IDictionary<string, object>;
var props = TypeDescriptor.GetProperties(anon);
foreach (PropertyDescriptor p in props)
dyn.Add(p.Name, p.GetValue(anon));
return dyn;
}
// использование
var anon = new { Name = "John", Age = 30 };
dynamic dyn = ToDynamic(anon);
Console.WriteLine(
$"Name: {dyn.Name}, Age: {dyn.Age}");
dyn.Age = 35;
dyn.City = "New York";
Console.WriteLine(
@$"Name: {dyn.Name},
Age: {dyn.Age},
City:{dyn.City}");
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍7👎1
День 1807. #TipsAndTricks
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
class ObjectPool<T> where T : new()
{
private readonly ConcurrentBag<T> _objects;
private readonly Func<T> _generator;
public ObjectPool(Func<T>? generator = null)
{
_generator = generator ??
(() => new T());
_objects = [];
}
public T Get()
{
return _objects.TryTake(out T item)
? item
: _generator();
}
public void Return(T item)
{
_objects.Add(item);
}
}
class Expensive
{
public int Value { get; set; }
}
// использование
var pool = new ObjectPool<Expensive>();
var resource = pool.Get();
resource.Value = 42;
Console.WriteLine(
$"Значение ресурса: {resource.Value}");
pool.Return(resource);
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍9👎1