Работаете днём в корпоративном репо, вечером в личном на GitHub. Один ПК, но email и ключи подписи разные. Ручная смена настроек Git каждый раз бесит и приводит к косякам с коммитами.
Условные includeIf решают всё
Добавьте в ~/.gitconfig пару строк. Git сам подхватит нужный конфиг по пути к папке или remote URL.
Для рабочих папок загрузит корпоративный email:
[includeIf "gitdir:~/work/"] path = .gitconfig-corp
Личные репозитории получат ваш домашний email автоматически:
[includeIf "hasconfig:remote.*.url:https://github.com/вашеимя/**"] path = .gitconfig-personal
Забудьте про git config --global user.email каждый раз.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🤩6🌚2❤1
Удобство и гибкость метапрограммирования — теперь nameof работает для обобщений без аргументов.
Благодаря новой поддержке можно получить имена обобщенных типов без указания параметров:
Console.WriteLine(nameof(List<>)); // Выведет "List"
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15
string — это ссылочный тип с особой поддержкой рантайма. Внутри он хранит непрерывный блок UTF-16 кодовых единиц.
Отсюда первый сюрприз:
string s = "😊";
Console.WriteLine(s.Length); // 2, не 1
Length считает не символы, а UTF-16 кодовые единицы. Эмодзи и многие Unicode-символы занимают две единицы — суррогатную пару.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🥱6😢1
Если коллекция после создания больше не изменяется, обычный Dictionary<TKey, TValue> не всегда лучший вариант.
В .NET 8 появились FrozenDictionary и FrozenSet — специализированные неизменяемые коллекции для сценариев, где данные сначала собираются, а потом много раз читаются.
var users = new Dictionary<int, string>
{
[1] = "Ada",
[2] = "Linus",
[3] = "Grace"
};
var frozenUsers = users.ToFrozenDictionary();
После создания frozenUsers нельзя изменить. Зато структура оптимизируется именно под быстрый поиск.
FrozenDictionary не означает «быстрее любого Dictionary». Создание такой коллекции дороже, поэтому выигрыш появляется именно там, где чтений много, а изменений нет.
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4
🧪 MSTest 4.4 теперь поддерживает Native AOT
Тесты тоже должны проверять то, что мы отправляем в production. Native AOT означает trimming, ограничения reflection и отсутствие runtime code generation. Обычный managed-тест может этого не показать.
В MSTest 4.4 появился source generation для тестов — test discovery и запуск можно выполнять без зависимости от runtime reflection.
В итоге можно прогнать тесты в тех же AOT-условиях, в которых будет работать приложение.
Особенно полезно для проектов, где Native AOT — часть production-сборки.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#async_news
Тесты тоже должны проверять то, что мы отправляем в production. Native AOT означает trimming, ограничения reflection и отсутствие runtime code generation. Обычный managed-тест может этого не показать.
В MSTest 4.4 появился source generation для тестов — test discovery и запуск можно выполнять без зависимости от runtime reflection.
<PublishAot>true</PublishAot>
В итоге можно прогнать тесты в тех же AOT-условиях, в которых будет работать приложение.
Особенно полезно для проектов, где Native AOT — часть production-сборки.
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7
В C# мы пишем:
int Double(int x) => x * 2;
var result = Double(21);
Но JIT может решить, что отдельный вызов здесь вообще не нужен.
При inlining тело метода встраивается непосредственно в вызывающий код:
call Double
↓
x * 2
21 * 2
Зачем? Меньше накладных расходов на вызов метода, а главное — JIT получает больше кода для последующих оптимизаций. Но inline не означает «метод маленький → обязательно встроить».
JIT учитывает размер метода, частоту вызовов, доступность кода для анализа и другие эвристики. Поэтому даже небольшой метод может не попасть в inline.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
— это подсказка JIT, а не приказ.
И наоборот, иногда большой метод всё-таки может быть встроен, если JIT считает это выгодным.
Вот почему производительность C# нельзя оценивать только по исходному коду: между C# и CPU есть ещё один слой — JIT и его оптимизатор.
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤🔥3
8 сентября Microsoft выпустила первый RC .NET 11. У него уже есть Go-Live Support, поэтому сборку можно использовать в production — хотя для критичных систем разумнее дождаться финального релиза.
Что главное:
→ C# 15 стал default для .NET 11 — union types и closed hierarchies больше не preview;
→ runtime получил новые возможности для диагностики и оптимизации, включая crash reporting на Unix и аппаратное ускорение System.Half;
→ обновились SDK, MSBuild, NuGet, ASP.NET Core, MAUI и WinForms.
А 10 ноября заканчивается поддержка .NET 8 и .NET 9. Для них уже вышли сентябрьские security updates: 8.0.31, 9.0.20 и 10.0.12.
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11😢2
Task — это обещание, что какое-то событие произойдёт в будущем. Не результат, а именно обещание.
Task<T> — то же самое, но с результатом типа T. Когда задача завершится, значение станет доступно.
Задача считается завершённой, когда событие произошло или значение получено. Проверить это можно через IsCompleted или Status.
Правильно — асинхронно:
• await task — отпускает поток, пока задача не завершится. Поток свободен, приложение не зависает.
• ContinueWith(action) — задача сама вызовет ваш колбэк, когда завершится. Полезно, если нужно выстроить цепочку без await.
Неправильно — синхронно:
• task.Wait() и task.Result блокируют текущий поток до завершения задачи. Это неэффективно, ведь поток простаивает и опасно: риск дедлока в UI и ASP.NET-контекстах.
Единственное исключение: вызывать Wait или Result на уже завершённой задаче безопасно и дёшево — результат уже есть, блокировки не будет.
async — это флаг для компилятора: «в этом методе может встретиться await, подготовь конечный автомат». Не более того.
async без await внутри — бессмысленный метод. Он выполнится полностью синхронно, а компилятор при этом сгенерирует тяжёлый шаблонный код конечного автомата впустую.
async не запускает код в фоновом потоке. Если нужен фоновый поток — используйте Task.Run.
await — точка, в которой метод «разрезается» на до и после. Если задача ещё не завершена, управление возвращается вызывающему коду. Когда задача завершится — выполнение продолжится с того же места.
Без await ключевое слово async ничего не делает.
Вызов .Result или .Wait() в UI-потоке — дедлок. UI-поток заблокирован ожиданием, а задача не может завершиться, потому что ей нужен тот же UI-поток.
async void — допустим только для обработчиков событий. В остальных случаях используйте async Task. Исключения из async void невозможно поймать извне.
Забыли await — задача запустится, но никто не дождётся её завершения. Ошибки внутри неё потеряются молча.
async без await — компилятор предупредит, и правильно сделает. Метод синхронный, а обёртка в Task создаёт ложные ожидания у вызывающего кода.
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤19👍4🥱1
Что реально хочется разобрать? Где чаще всего застреваете? Что пробовали, но так и не получилось нормально внедрить?
Задавайте всё, что давно хотелось спросить
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
This media is not supported in your browser
VIEW IN TELEGRAM
С 14 сентября цены в Proglib Academy вырастут
Так что если курс давно ждёт своего часа — лучше не откладывать ещё на месяц
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
В FluentValidation условная валидация запускает правила только при нужных условиях. When и Unless экономят циклы и делают валидаторы читаемыми.
• When = «проверить, если условие верно»
• Unless = «проверить, если условие НЕ верно»
Пример:
RuleFor(x => x.ShippingAddress)
.NotEmpty()
.When(x => x.DeliveryMethod == "Express");
RuleFor(x => x.CreditCard)
.NotEmpty()
.Unless(x => x.PaymentMethod == "PayPal");
When проверяет условие перед правилом. Express доставка требует адрес, PayPal не требует карту. Логика в одном месте без if-else в контроллере.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
С днем программиста, коллеги!
Желаем, чтобы код компилировался с первого раза, баги находились до релиза, а таски «на пять минут» действительно занимали пять минут.
И конечно, пусть в жизни будет больше приятных сюрпризов — один из них мы уже подготовили. Скорее переходите по ссылке, трясите коробку и забирайте свой подарок ко Дню программиста: https://tprg.ru/YtJ2
Желаем, чтобы код компилировался с первого раза, баги находились до релиза, а таски «на пять минут» действительно занимали пять минут.
И конечно, пусть в жизни будет больше приятных сюрпризов — один из них мы уже подготовили. Скорее переходите по ссылке, трясите коробку и забирайте свой подарок ко Дню программиста: https://tprg.ru/YtJ2
🥰15❤9🎉4👍1
.NET Core на .NET 11В C# 15 можно описать тип с несколькими допустимыми вариантами:
public union IntOrString(int, string);
Такой endpoint может вернуть либо число, либо строку:
app.MapGet("/max-unavailable", () => new IntOrString(2));
JSON останется обычным:
2
или:
"25%"
Без $type и дополнительных обёрток.
Главный плюс — компилятор проверяет, что switch обработал все варианты union.
Поддерживается в Minimal APIs, MVC, SignalR, Blazor и OpenAPI.
📍 Навигация: Вакансии • Задачи • Собесы
#async_news
Please open Telegram to view this post
VIEW IN TELEGRAM
❤🔥20❤3
Разберём на вебинаре, как тратить меньше на AI-агентов — без потери качества кода.
— когда дорогая модель действительно нужна, а когда хватит дешёвой;
— сколько стоит один прогон и куда уходят токены;
— какие задачи можно отдавать субагентам;
— как настроить маршрутизацию моделей;
— что проверять в AI-коде перед merge.
Please open Telegram to view this post
VIEW IN TELEGRAM
😁4🥱2🔥1
🧑💻 Забытый await: от race condition до DoS-атаки
В C# ключевое слово async — это инструкция компилятору превратить метод в state machine. await отмечает точку логической приостановки. Всё просто: выполнение не должно продолжаться, пока результат не существует.
При await весь код выполняется синхронно, пока не достигнет I/O или функции, возвращающей Task. Если Task не завершён, происходит раскрутка кадра стека, и поскольку обрамляющие методы приостановлены до самого верха, поток может быть освобождён в пул потоков.
Позже задача завершается, продолжение планируется и выполнение возобновляется там, где остановилось. Это не параллельное выполнение, это приостановка и возобновление.
Если забыли await:
Это режим «запустил и забыл» (fire-and-forget). Последствия:
• выполнение продолжается немедленно
• зависимость не обеспечивается
• исключения выходят за пределы логического стека вызовов
Что может произойти с приложением
⚠️ Забытый await может привести к классическому состоянию гонки: код продолжает выполнение до завершения критической операции, например, проверки прав доступа, открывая окно для эксплуатации.
Атакующий может успеть получить доступ к данным между моментом начала проверки и её завершением.
⚠️ Когда исключения выходят за пределы логического стека вызовов, они могут быть проглочены без логирования. Это маскирует неудачные попытки аутентификации, SQL-инъекции или другие атаки.
⚠️ Истощение пула потоков как вектор DoS-атаки. При высокой нагрузке неправильное использование async/await приводит к истощению пула потоков. Злоумышленник может намеренно генерировать запросы, эксплуатирующие блокирующие операции.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#il_люминатор
В C# ключевое слово async — это инструкция компилятору превратить метод в state machine. await отмечает точку логической приостановки. Всё просто: выполнение не должно продолжаться, пока результат не существует.
При await весь код выполняется синхронно, пока не достигнет I/O или функции, возвращающей Task. Если Task не завершён, происходит раскрутка кадра стека, и поскольку обрамляющие методы приостановлены до самого верха, поток может быть освобождён в пул потоков.
Позже задача завершается, продолжение планируется и выполнение возобновляется там, где остановилось. Это не параллельное выполнение, это приостановка и возобновление.
Если забыли await:
var task = GetDataAsync();
// забыли await
Use(task);
Это режим «запустил и забыл» (fire-and-forget). Последствия:
• выполнение продолжается немедленно
• зависимость не обеспечивается
• исключения выходят за пределы логического стека вызовов
Что может произойти с приложением
Атакующий может успеть получить доступ к данным между моментом начала проверки и её завершением.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4😁2🥱2
kubectl top pod показывает 1 ГБ, а managed heap занимает всего 300 МБ. Куда делись остальные 700?
Память .NET-процесса — это не только GC:
— managed heap;
— JIT и загруженные сборки;
— стеки потоков;
— native-библиотеки;
— буферы сокетов;
— runtime и TLS.
Поэтому одного GCHeapHardLimitPercent недостаточно: он ограничивает только managed heap.
kubectl top pod
dotnet-counters monitor -p <pid> System.Runtime
dotnet-gcdump collect -p <pid>
dotnet-dump collect -p <pid>
→ не выставляй всем подам одинаковые memory limits;
→ проверь AsNoTracking() и проекции в EF Core;
→ ищи .Result, .Wait() и блокирующий I/O;
→ тестируй GC под реальным memory limit контейнера.
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
В TypeScript достаточно, чтобы у объекта был нужный метод:
function foo(a: { Do(): void }) {
a.Do();
}
class A
{
public void Do() => Console.WriteLine("A.Do");
}
class B
{
public void Do() => Console.WriteLine("B.Do");
}
Foo(new A());
Foo(new B());
И здесь на сцену выходят interceptors — экспериментальная возможность компилятора C#.
[DuckShape]
public interface IDoable
{
void Do();
}
public static partial class Ops
{
[DuckTyped]
public static void Foo(IDoable value)
=> value.Do();
}
internal readonly struct ShapeAdapter_IDoable_A : IDoable
{
private readonly A _value;
public ShapeAdapter_IDoable_A(A value)
=> _value = value;
public void Do() => _value.Do();
}
А interceptor подменяет вызов и оборачивает A или B в подходящий адаптер.
Ops.Foo(new A()); // A.Do
Ops.Foo(new B()); // B.Do
Работает и с properties — например, можно описать INameable с Name { get; set; }, а затем передать обычный Person, не меняя его код.
⚠️ Но это скорее интересный PoC, чем готовая замена интерфейсам:
— interceptors остаются экспериментальной возможностью;
— нужен source generator;
— есть ограничения и не покрытые edge cases;
— появляется дополнительная магия на этапе компиляции.
📍 Навигация: Вакансии • Задачи • Собесы
#sharp_view
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4❤2
.NET Core: 3 ловушкиMiddleware — звено HTTP pipeline:
app.Use(async (context, next) =>
{
Console.WriteLine("Request");
await next(context);
Console.WriteLine("Response");
});
await _next(context);
Если его не вызвать, middleware ниже и endpoint не выполнятся.
Middleware переиспользуется между запросами, поэтому Scoped лучше получать в InvokeAsync:
public async Task InvokeAsync(
HttpContext context,
IMyScopedService service)
{
await _next(context);
}
await _next(context);
context.Response.StatusCode = 503; // 💥
Если response уже отправляется, будет InvalidOperationException.
И ещё: не передавайте scoped-сервис в Task.Run после завершения запроса — его scope уже мог быть уничтожен. Для фоновой работы создавайте новый scope или используйте BackgroundService
📍 Навигация: Вакансии • Задачи • Собесы
#il_люминатор
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6