Разбираем 7 инструментов HttpClient!
HttpClient пригодится не только для простого GET. С ним можно проверять HTTP-статус, отправлять JSON, читать ответ как текст или поток и собирать запросы с нужным методом и заголовками.
В этой шпоре собраны GetAsync, EnsureSuccessStatusCode, ReadAsStringAsync, GetFromJsonAsync, PostAsJsonAsync, SendAsync и GetStreamAsync.
➡️ C# Ready | #шпора
HttpClient пригодится не только для простого GET. С ним можно проверять HTTP-статус, отправлять JSON, читать ответ как текст или поток и собирать запросы с нужным методом и заголовками.
В этой шпоре собраны GetAsync, EnsureSuccessStatusCode, ReadAsStringAsync, GetFromJsonAsync, PostAsJsonAsync, SendAsync и GetStreamAsync.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8🤝4❤3👍2
Почему копия record через with может изменить исходные данные?
Выражение with удобно для создания нового record с одним изменённым полем. Кажется, что после копирования два объекта полностью независимы.
Представим профиль со списком тегов:
Создадим копию с другим именем:
Сам record действительно новый. Но with делает поверхностную копию, поэтому поле Tags в обоих объектах указывает на один и тот же список.
Теперь изменим теги только у копии:
Результат у исходного профиля тоже станет dev, admin. С новым именем всё в порядке, проблема только во вложенном изменяемом объекте.
Если нужен отдельный список, явно создайте его при копировании:
Теперь добавление в independent.Tags не изменит original.Tags. Но это копия только самого списка. Если элементы списка тоже изменяемые объекты, их придётся копировать отдельно.
➡️ C# Ready | #совет
Выражение with удобно для создания нового record с одним изменённым полем. Кажется, что после копирования два объекта полностью независимы.
Представим профиль со списком тегов:
record Profile(string Name, List<string> Tags);
var original = new Profile("Ana", new() { "dev" });
Создадим копию с другим именем:
var copy = original with { Name = "Bob" };Сам record действительно новый. Но with делает поверхностную копию, поэтому поле Tags в обоих объектах указывает на один и тот же список.
Теперь изменим теги только у копии:
copy.Tags.Add("admin");
Console.WriteLine(string.Join(", ", original.Tags));Результат у исходного профиля тоже станет dev, admin. С новым именем всё в порядке, проблема только во вложенном изменяемом объекте.
Если нужен отдельный список, явно создайте его при копировании:
var independent = original with
{
Tags = new List<string>(original.Tags)
};
Теперь добавление в independent.Tags не изменит original.Tags. Но это копия только самого списка. Если элементы списка тоже изменяемые объекты, их придётся копировать отдельно.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Знали, когда в C# удобно использовать Random.Shared?
Для простого случайного числа нередко создают отдельный генератор прямо в месте вызова:
Такой код работает, но при частых вызовах приходится снова и снова создавать объект. Если генератор нужен во многих местах приложения, проще взять общий экземпляр:
Random.Shared доступен начиная с .NET 6 и безопасен для одновременного использования из разных потоков. Для обычных игровых событий, тестовых данных и случайного выбора этого достаточно.
Обратите внимание на границы Next. Нижняя включается, верхняя нет:
Так можно выбрать индекс, не выйдя за пределы списка. Для шестигранного кубика верхняя граница должна быть 7, а не 6:
А вот пароли, токены и коды подтверждения этим способом генерировать нельзя. Для значений, которые должны быть непредсказуемы для атакующего, нужен криптографический генератор:
Random.Shared хорош для обычной случайности, а RandomNumberGenerator нужен там, где от случайности зависит безопасность.
➡️ C# Ready | #совет
Для простого случайного числа нередко создают отдельный генератор прямо в месте вызова:
int dice = new Random().Next(1, 7);
Такой код работает, но при частых вызовах приходится снова и снова создавать объект. Если генератор нужен во многих местах приложения, проще взять общий экземпляр:
int dice = Random.Shared.Next(1, 7);
Random.Shared доступен начиная с .NET 6 и безопасен для одновременного использования из разных потоков. Для обычных игровых событий, тестовых данных и случайного выбора этого достаточно.
Обратите внимание на границы Next. Нижняя включается, верхняя нет:
int index = Random.Shared.Next(0, items.Count);
Так можно выбрать индекс, не выйдя за пределы списка. Для шестигранного кубика верхняя граница должна быть 7, а не 6:
int dice = Random.Shared.Next(1, 7);
А вот пароли, токены и коды подтверждения этим способом генерировать нельзя. Для значений, которые должны быть непредсказуемы для атакующего, нужен криптографический генератор:
int code = RandomNumberGenerator.GetInt32(100000, 1000000);
Random.Shared хорош для обычной случайности, а RandomNumberGenerator нужен там, где от случайности зависит безопасность.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4🔥4
Почему цепочка LINQ иногда читает всю коллекцию до первого результата?
Where и Select могут отдавать элементы по одному. А вот OrderBy, GroupBy и Join устроены сложнее, им нужно подготовить данные, прежде чем продолжить обработку. Автор разбирает эту границу между ленивым вычислением и буферизацией.
В статье показывают:
• зачем OrderBy хранит элементы и сортирует индексы
• как GroupBy собирает группы и сохраняет порядок их появления
• почему Join сначала строит структуру для внутренней коллекции
Можно понять куда уходит память в длинных цепочках и почему перестановка операций иногда меняет стоимость запроса.
➡️ C# Ready | #статья
Where и Select могут отдавать элементы по одному. А вот OrderBy, GroupBy и Join устроены сложнее, им нужно подготовить данные, прежде чем продолжить обработку. Автор разбирает эту границу между ленивым вычислением и буферизацией.
В статье показывают:
• зачем OrderBy хранит элементы и сортирует индексы
• как GroupBy собирает группы и сохраняет порядок их появления
• почему Join сначала строит структуру для внутренней коллекции
Можно понять куда уходит память в длинных цепочках и почему перестановка операций иногда меняет стоимость запроса.
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥6👍3
Шпаргалка по источникам памяти для Span<T> в C#!
На картинке рядом показаны три способа создать Span<T>. Он может ссылаться на обычный массив, область stackalloc или неуправляемый участок памяти. Отдельные примеры помогают увидеть, что Span<T> описывает диапазон, а не владеет памятью.
Сохрани, чтобы не потерять!
➡️ C# Ready | #ресурс
На картинке рядом показаны три способа создать Span<T>. Он может ссылаться на обычный массив, область stackalloc или неуправляемый участок памяти. Отдельные примеры помогают увидеть, что Span<T> описывает диапазон, а не владеет памятью.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3❤1👍1
🎓 Как гарантированно войти в сферу кибербезопасности с официальным дипломом?
Самостоятельное обучение в ИБ - это месяцы хаоса и разрозненных роликов с YouTube. Покупка курсов по отдельности на популярных платформах легко переваливает за 300 000 ₽.
🔥CyberYozh Academy собрали цельный карьерный трек в один пак: 5 фундаментальных программ + официальный Диплом системного администратора за 50 000 ₽ (вместо ~250 000 ₽)
Что входит в программу:
▪️ Linux CyberPunk - системное администрирование с выдачей диплома, открывающего двери в корпоративный сектор.
▪️ SQL для хакера - глубокое понимание архитектуры баз данных и векторов атак.
▪️ HackerPoint - продвинутый уровень пентеста и анализа уязвимостей.
▪️ HackerPoint (Blue vs Red Team) - база сетевой безопасности, защиты и атак.
▪️ AI-помощники на Python - автоматизация рутины безопасника при помощи нейросетей.
Вы получаете законченную квалификацию: от администрирования защищенных облачных решений до проведения тестов на проникновение.
⚠️ Обратите внимание: Из-за персональной проверки заданий и официального оформления дипломов на каждый поток действует жесткий лимит - не более 15 мест.
🦔 Отправь промокод
👉@cyacademy_support
Самостоятельное обучение в ИБ - это месяцы хаоса и разрозненных роликов с YouTube. Покупка курсов по отдельности на популярных платформах легко переваливает за 300 000 ₽.
🔥CyberYozh Academy собрали цельный карьерный трек в один пак: 5 фундаментальных программ + официальный Диплом системного администратора за 50 000 ₽
Что входит в программу:
▪️ Linux CyberPunk - системное администрирование с выдачей диплома, открывающего двери в корпоративный сектор.
▪️ SQL для хакера - глубокое понимание архитектуры баз данных и векторов атак.
▪️ HackerPoint - продвинутый уровень пентеста и анализа уязвимостей.
▪️ HackerPoint (Blue vs Red Team) - база сетевой безопасности, защиты и атак.
▪️ AI-помощники на Python - автоматизация рутины безопасника при помощи нейросетей.
Вы получаете законченную квалификацию: от администрирования защищенных облачных решений до проведения тестов на проникновение.
⚠️ Обратите внимание: Из-за персональной проверки заданий и официального оформления дипломов на каждый поток действует жесткий лимит - не более 15 мест.
C#Ready в чат с менеджером, чтобы закрепить за собой скидку и узнать подробности: 👉@cyacademy_support
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Почему await Task.WhenAll может показать не все ошибки сразу?
Допустим несколько независимых операций выполняются параллельно. Мы объединяем их в одну задачу:
Если обе операции завершатся с ошибками, WhenAll дождётся обеих. Итоговая задача будет содержать исключения из всех упавших операций.
Для диагностики сохраните общую задачу перед await:
Теперь можно дождаться её и обработать неудачу:
Свойство all.Exception содержит AggregateException. Его InnerExceptions позволяют увидеть ошибки нескольких операций, а не только ту, которую выбросил await. Например, внутри обработки именно ошибочной задачи можно пройтись по ним:
Знак ! здесь уместен только внутри ветки, где общая задача действительно завершилась с ошибкой. Если она была отменена без ошибок, Exception будет null, и обработку отмены нужно делать отдельно.
Почему await Task.WhenAll может показать не все ошибки сразу?
Допустим, несколько независимых операций выполняются параллельно. Мы объединяем их в одну задачу:
Если обе операции завершатся с ошибками, WhenAll дождётся обеих. Итоговая задача будет содержать исключения из всех упавших операций. Но обычный await выбрасывает одно исключение, поэтому в catch легко увидеть только часть картины.
Для диагностики сохраните общую задачу перед await:
Теперь можно дождаться её и обработать неудачу:
Свойство all.Exception содержит AggregateException. Его InnerExceptions позволяют увидеть ошибки нескольких операций, а не только ту, которую выбросил await. Например, внутри обработки именно ошибочной задачи можно пройтись по ним:
Знак ! здесь уместен только внутри ветки, где общая задача действительно завершилась с ошибкой. Если она была отменена без ошибок, Exception будет null, и обработку отмены нужно делать отдельно.
➡️ C# Ready | #совет
Допустим несколько независимых операций выполняются параллельно. Мы объединяем их в одну задачу:
var all = Task.WhenAll(first, second);
await all;
Если обе операции завершатся с ошибками, WhenAll дождётся обеих. Итоговая задача будет содержать исключения из всех упавших операций.
Для диагностики сохраните общую задачу перед await:
var all = Task.WhenAll(first, second);
Теперь можно дождаться её и обработать неудачу:
try
{
await all;
}
catch
{
Console.Error.WriteLine(all.Exception);
throw;
}
Свойство all.Exception содержит AggregateException. Его InnerExceptions позволяют увидеть ошибки нескольких операций, а не только ту, которую выбросил await. Например, внутри обработки именно ошибочной задачи можно пройтись по ним:
foreach (var error in all.Exception!.InnerExceptions)
Console.WriteLine(error.Message);
Знак ! здесь уместен только внутри ветки, где общая задача действительно завершилась с ошибкой. Если она была отменена без ошибок, Exception будет null, и обработку отмены нужно делать отдельно.
Почему await Task.WhenAll может показать не все ошибки сразу?
Допустим, несколько независимых операций выполняются параллельно. Мы объединяем их в одну задачу:
var all = Task.WhenAll(first, second);
await all;
Если обе операции завершатся с ошибками, WhenAll дождётся обеих. Итоговая задача будет содержать исключения из всех упавших операций. Но обычный await выбрасывает одно исключение, поэтому в catch легко увидеть только часть картины.
Для диагностики сохраните общую задачу перед await:
var all = Task.WhenAll(first, second);
Теперь можно дождаться её и обработать неудачу:
try
{
await all;
}
catch
{
Console.Error.WriteLine(all.Exception);
throw;
}
Свойство all.Exception содержит AggregateException. Его InnerExceptions позволяют увидеть ошибки нескольких операций, а не только ту, которую выбросил await. Например, внутри обработки именно ошибочной задачи можно пройтись по ним:
foreach (var error in all.Exception!.InnerExceptions)
Console.WriteLine(error.Message);
Знак ! здесь уместен только внутри ветки, где общая задача действительно завершилась с ошибкой. Если она была отменена без ошибок, Exception будет null, и обработку отмены нужно делать отдельно.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤2👍2
Альфа-Банк ищет скиллового коллегу QA Fullstack на C#
Предлагаем полную удалёнку или гибридный график – у нас классные ИТ-офисы в Москве, Питере и Екатеринбурге.
Что нужно делать
– Разрабатывать автотесты на C#.
– Запускать функциональные, регрессионные и интеграционные тесты, включая API.
– Снижать количество дефектов в диалоге с разработкой и бизнесом.
Какие навыки важны
– Опыт автоматизации на C# и ручного тестирования от года.
– Знание API (SOAP, REST), SQL, основ клиент-серверной архитектуры и тест-дизайна.
Условия и развитие
– Сильное сообщество QA и C#-разработчиков: можно получить совет и поделиться опытом на митапе и в блоге на Хабре.
– Реальная перспектива роста: технический трек развития, в том числе до C#-лида
Оставляйте отклик по ссылке.
Предлагаем полную удалёнку или гибридный график – у нас классные ИТ-офисы в Москве, Питере и Екатеринбурге.
Что нужно делать
– Разрабатывать автотесты на C#.
– Запускать функциональные, регрессионные и интеграционные тесты, включая API.
– Снижать количество дефектов в диалоге с разработкой и бизнесом.
Какие навыки важны
– Опыт автоматизации на C# и ручного тестирования от года.
– Знание API (SOAP, REST), SQL, основ клиент-серверной архитектуры и тест-дизайна.
Условия и развитие
– Сильное сообщество QA и C#-разработчиков: можно получить совет и поделиться опытом на митапе и в блоге на Хабре.
– Реальная перспектива роста: технический трек развития, в том числе до C#-лида
Оставляйте отклик по ссылке.
❤3🔥1
Читаем большой JSON-массив по одному объекту в C#!
Пусть orders.json содержит тысячи заказов одним массивом. Если загрузить файл целиком через ReadAllText, память займут и исходный текст, и созданные объекты. Для последовательной обработки удобнее читать поток и получать элементы по мере разбора.
Сначала опишем модель входных данных. В примере у каждого заказа есть номер и сумма:
Откроем файл как поток. JsonSerializer.DeserializeAsyncEnumerable ожидает JSON-массив и возвращает асинхронную последовательность его элементов:
Здесь cancellationToken позволяет прервать длительную обработку, например при остановке запроса или фоновой задачи. Не надо самостоятельно резать JSON на строки, переносы строк не определяют границы объектов.
Обработаем элементы по одному и накопим итог. Нулевые элементы пропустим, а отрицательную сумму считаем ошибкой данных:
После завершения цикла в total будет сумма всех валидных заказов. В нужном месте вместо накопления можно отправлять запись в базу. Если важна устойчивость к повторной обработке, используй Id как ключ идемпотентности.
➡️ C# Ready | #практика
Пусть orders.json содержит тысячи заказов одним массивом. Если загрузить файл целиком через ReadAllText, память займут и исходный текст, и созданные объекты. Для последовательной обработки удобнее читать поток и получать элементы по мере разбора.
Сначала опишем модель входных данных. В примере у каждого заказа есть номер и сумма:
public sealed record Order(int Id, decimal Total);
Откроем файл как поток. JsonSerializer.DeserializeAsyncEnumerable ожидает JSON-массив и возвращает асинхронную последовательность его элементов:
await using var stream = File.OpenRead("orders.json");
var orders = JsonSerializer.DeserializeAsyncEnumerable<Order>(
stream, cancellationToken: cancellationToken);Здесь cancellationToken позволяет прервать длительную обработку, например при остановке запроса или фоновой задачи. Не надо самостоятельно резать JSON на строки, переносы строк не определяют границы объектов.
Обработаем элементы по одному и накопим итог. Нулевые элементы пропустим, а отрицательную сумму считаем ошибкой данных:
decimal total = 0;
await foreach (Order? order in orders) {
if (order is null) continue;
if (order.Total < 0) throw new InvalidDataException("Сумма меньше нуля");
total += order.Total;
}
После завершения цикла в total будет сумма всех валидных заказов. В нужном месте вместо накопления можно отправлять запись в базу. Если важна устойчивость к повторной обработке, используй Id как ключ идемпотентности.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥4❤2