This media is not supported in your browser
VIEW IN TELEGRAM
На GitHub есть репозиторий free-programming-books, где собрано более 4000 бесплатных книг, 2000 курсов и других полезных ресурсов по программированию
Для удобства поиска можно использовать этот инструмент
Этот проект - яркий пример силы опенсорс сообщества, который из клона списка со StackOverflow стал одним из самых популярных на GitHub✌️
⏩ Русскоязычная версия ресурсов
👉 @KodBlog
Для удобства поиска можно использовать этот инструмент
Этот проект - яркий пример силы опенсорс сообщества, который из клона списка со StackOverflow стал одним из самых популярных на GitHub
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3
Одна из самых недооценённых фич в .NET — System.Threading.Channels.
С её помощью можно просто построить асинхронное взаимодействие через сообщения на C#. У Channel есть два API:
- для записи сообщений в канал,
- для асинхронного чтения сообщений из канала.
Можно запустить воркер в фоне, который будет забирать сообщения и передавать их на обработку. На этой основе легко собрать примитивный in-memory message bus (со всеми очевидными ограничениями).
Пример реализации тут
👉 @KodBlog
С её помощью можно просто построить асинхронное взаимодействие через сообщения на C#. У Channel есть два API:
- для записи сообщений в канал,
- для асинхронного чтения сообщений из канала.
Можно запустить воркер в фоне, который будет забирать сообщения и передавать их на обработку. На этой основе легко собрать примитивный in-memory message bus (со всеми очевидными ограничениями).
Пример реализации тут
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3
Как упаковать RPM на Linux через GitLab CI/CD?
В материале детально разбирается разработка службы на .NET Core, конфигурация spec-файла, применение макросов и запуск GitLab Runner внутри Docker. Полезные рекомендации для DevOps по автоматизации билда и контролю версий.
Читать подробнее: https://habr.com/ru/articles/952748/
👉 @KodBlog
В материале детально разбирается разработка службы на .NET Core, конфигурация spec-файла, применение макросов и запуск GitLab Runner внутри Docker. Полезные рекомендации для DevOps по автоматизации билда и контролю версий.
Читать подробнее: https://habr.com/ru/articles/952748/
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍2
Чистим NuGet и освобождаем десятки гигабайт
Со временем NuGet накапливает огромные объёмы кэша:
- http-cache может весить 10+ ГБ
- global-packages легко разрастается до 50 ГБ и больше
Если места на диске не хватает — кэши можно безопасно почистить:
Очистка глобальных пакетов:
Очистка http-кэша:
Очистка временных файлов:
Полная зачистка всего:
После очистки пакеты будут скачаны заново при следующем dotnet restore, но на диске станет намного свободнее.
Проверить текущие кэши можно так:
👉 @KodBlog
Со временем NuGet накапливает огромные объёмы кэша:
- http-cache может весить 10+ ГБ
- global-packages легко разрастается до 50 ГБ и больше
Если места на диске не хватает — кэши можно безопасно почистить:
Очистка глобальных пакетов:
dotnet nuget locals global-packages --clear
Очистка http-кэша:
dotnet nuget locals http-cache --clear
Очистка временных файлов:
dotnet nuget locals temp --clear
Полная зачистка всего:
dotnet nuget locals all --clear
После очистки пакеты будут скачаны заново при следующем dotnet restore, но на диске станет намного свободнее.
Проверить текущие кэши можно так:
dotnet nuget locals all --list
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25👍7❤5
Python vs C#
Два языка — две совершенно разные цепочки выполнения:
C# (.NET Runtime):
🔹 Исходный код → Intermediate Language (IL) → сборки (.dll / .exe)
🔹 Сборки проверяются и загружаются в .NET Runtime
🔹 JIT- или AOT-компилятор переводит IL в машинный код
🔹 Мощные инструменты и статическая типизация делают выполнение предсказуемым и быстрым
Python (CPython Runtime):
🔸 Исходный код → байткод (кэшируется в .pyc, если возможно)
🔸 Система импорта подгружает модули и зависимости во время выполнения
🔸 Python Virtual Machine (PVM) интерпретирует байткод “на лету”
🔸 Гибкий и динамичный язык, но скорость зависит от накладных расходов интерпретации
Баланс очевиден:
• C# оптимизирован под производительность и безопасность
• Python — под гибкость и скорость итераций
Разные подходы, разные сильные стороны, именно поэтому оба языка процветают в своих экосистемах.
👉 @KodBlog
Два языка — две совершенно разные цепочки выполнения:
C# (.NET Runtime):
Python (CPython Runtime):
Баланс очевиден:
• C# оптимизирован под производительность и безопасность
• Python — под гибкость и скорость итераций
Разные подходы, разные сильные стороны, именно поэтому оба языка процветают в своих экосистемах.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤5🤣3🔥2
Одна из частых проблем у многих программистов — они используют async и await, не понимая, что на самом деле происходит.
Асинхронная задача не блокирует основной поток, поэтому пока выполняется длительная операция, мы можем выполнять другие действия.
👉 @KodBlog
Асинхронная задача не блокирует основной поток, поэтому пока выполняется длительная операция, мы можем выполнять другие действия.
Console.WriteLine("Запуск программы");
// начинаем загрузку файла, длительность — 3 секунды
var downloadFileTask = DownloadFile();
// основной поток не блокируется — можем выполнять другие задачи
Console.WriteLine("Делаем другие вещи: обращаемся к БД, отправляем письма и т.д.");
// на этом этапе ожидаем завершения загрузки файла
await downloadFileTask;
Console.WriteLine("Программа завершена");
async Task DownloadFile()
{
Console.WriteLine("Загружаем файл...");
await Task.Delay(3000); // симулируем ожидание 3 секунды
Console.WriteLine("Файл загружен!");
}Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🤯5👍3🍌2
This media is not supported in your browser
VIEW IN TELEGRAM
MauiReactor — MVU для .NET MAUI
Открытая библиотека, реализующая паттерн Model View Update для .NET MAUI.
Она упрощает разработку кроссплатформенных приложений, ускоряет hot reload и повышает производительность.
MauiReactor предлагает альтернативу XAML и позволяет описывать интерфейс на C#. Поддерживаются темы, условный рендеринг и интеграция сторонних компонентов.
Подробнее
👉 @KodBlog
Открытая библиотека, реализующая паттерн Model View Update для .NET MAUI.
Она упрощает разработку кроссплатформенных приложений, ускоряет hot reload и повышает производительность.
MauiReactor предлагает альтернативу XAML и позволяет описывать интерфейс на C#. Поддерживаются темы, условный рендеринг и интеграция сторонних компонентов.
Подробнее
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3🤔1
Если у нас есть List<T> и IList<T>, то перебор элементов через List<T> выполняется быстрее, чем через IList<T>. Почему так происходит?
Результаты:
Откуда берутся эти 40 байт?
Когда вызывается List<T>.GetEnumerator() (а это происходит при каждой итерации foreach), возвращается структура под названием Enumerator.
Но при вызове IList<T>.GetEnumerator() возвращается переменная типа IEnumerator, которая содержит упакованную (boxed) версию этого перечислителя.
Вот откуда появляются дополнительные 40 байт аллокации и часть потери производительности.
Виртуальные вызовы методов
Есть и второй фактор.
При вызове IList<T>.GetEnumerator() вызывается виртуальный метод, а это медленнее, чем вызов невиртуального метода.
Причина проста: во время выполнения CLR должно определить, какой конкретный тип реально используется, и уже потом вызвать нужную реализацию метода.
В случае с List<T> этого делать не нужно — тип уже известен, и вызов идёт напрямую, без дополнительной диспетчеризации.
👉 @KodBlog
[MemoryDiagnoser]
public class Benchmark
{
private readonly List<int> numbersList = Enumerable.Range(0, 10_000).ToList();
private readonly IList<int> numbersIList = Enumerable.Range(0, 10_000).ToList();
[Benchmark(Baseline = true)]
public int GetSumOfList()
{
var sum = 0;
foreach (var number in numbersList) { sum += number; }
return sum;
}
[Benchmark]
public int GetSumOfIList()
{
var sum = 0;
foreach (var number in numbersIList) { sum += number; }
return sum;
}
}
Результаты:
Метод Среднее Ошибка Ст.откл. Отн. Отн.откл. Память Отн.память
------------- ---------- --------- ---------- ----- ---------- ------- ----------
GetSumOfList 4.214 μs 0.0836 μs 0.0782 μs 1.00 0.03 - NA
GetSumOfIList 19.508 μs 0.0459 μs 0.0384 μs 4.63 0.08 40 B NA
Откуда берутся эти 40 байт?
Когда вызывается List<T>.GetEnumerator() (а это происходит при каждой итерации foreach), возвращается структура под названием Enumerator.
Но при вызове IList<T>.GetEnumerator() возвращается переменная типа IEnumerator, которая содержит упакованную (boxed) версию этого перечислителя.
Вот откуда появляются дополнительные 40 байт аллокации и часть потери производительности.
Виртуальные вызовы методов
Есть и второй фактор.
При вызове IList<T>.GetEnumerator() вызывается виртуальный метод, а это медленнее, чем вызов невиртуального метода.
Причина проста: во время выполнения CLR должно определить, какой конкретный тип реально используется, и уже потом вызвать нужную реализацию метода.
В случае с List<T> этого делать не нужно — тип уже известен, и вызов идёт напрямую, без дополнительной диспетчеризации.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤18👍11🤯6🥴1
Этот пост вряд ли принесёт вам практическую пользу в повседневной жизни, ну, разве что улыбку. Давайте доведём ValueTuple до крайности ⌨️
Тип ValueTuple это удобный синтаксический сахар. Например, если у вас есть такой код, даже если вы назвали элементы кортежа:
Компилятор сгенерирует следующее:
То есть компилятор создаёт список Item1, Item2 и так далее для всех элементов кортежа. Но интересный момент возникает, если элементов больше 7:
Компилятор «понизит» это до:
После седьмого элемента компилятор создаёт новое свойство Rest, которое содержит «остальные» элементы. Если переборщить, например так:
И использовать так:
То получится что-то вроде
Если хотите поиграться с этим: sharplab.io
👉 @KodBlog
Тип ValueTuple это удобный синтаксический сахар. Например, если у вас есть такой код, даже если вы назвали элементы кортежа:
(var one, var two) = MyFunc();
Console.WriteLine(one + two);
(int ANumber, int AnotherNumber)
MyFunc() => (1,2);
Компилятор сгенерирует следующее:
[CompilerGenerated]
internal class Program
{
private static void <Main>$(string[] args)
{
ValueTuple<int, int> valueTuple = <<Main>$>g__MyFunc|0_0();
int item = valueTuple.Item1;
int item2 = valueTuple.Item2;
Console.WriteLine(item + item2);
}
[CompilerGenerated]
[return: TupleElementNames(new string[] { "ANumber", "AnotherNumber" })]
internal static ValueTuple<int, int> <<Main>$>g__MyFunc|0_0()
{
return new ValueTuple<int, int>(1, 2);
}
}
То есть компилятор создаёт список Item1, Item2 и так далее для всех элементов кортежа. Но интересный момент возникает, если элементов больше 7:
var (a,b,c,d,e,f,g,h) = MyFunc();
Console.WriteLine(a + b + c + d + e + f + g + h);
(int a, int b, int c, int d, int e, int f, int g, int h) MyFunc()
=> (1,2,3,4,5,6,7,8);
Компилятор «понизит» это до:
ValueTuple<int, int, int, int, int, int, int, ValueTuple<int>> valueTuple = <<Main>$>g__MyFunc|0_0();
int item = valueTuple.Item1;
int item2 = valueTuple.Item2;
int item3 = valueTuple.Item3;
int item4 = valueTuple.Item4;
int item5 = valueTuple.Item5;
int item6 = valueTuple.Item6;
int item7 = valueTuple.Item7;
int item8 = valueTuple.Rest.Item1;
Console.WriteLine(item + item2 + item3 + item4 + item5 + item6 + item7 + item8);
После седьмого элемента компилятор создаёт новое свойство Rest, которое содержит «остальные» элементы. Если переборщить, например так:
(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p, int q, int r, int s, int t, int u, int v, int w, int x, int y, int z, int aa, int bb, int cc, int dd, int ee, int ff, int gg, int hh, int ii, int jj, int kk, int ll, int mm, int nn, int oo, int pp, int qq, int rr, int ss, int tt, int uu, int vv, int ww, int xx) MyFunc()
{
return (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50);
}
И использовать так:
var (a,b,c,....xx) = MyFunc();
То получится что-то вроде
valueTuple.Rest.Rest.Rest.Rest.Item1Если хотите поиграться с этим: sharplab.io
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯11❤4👍4😁1
Rider 2025.2.3 был выпущен
Узнать об изменениях в этой сборке и скачать обновление можно здесь:
https://blog.jetbrains.com/dotnet/2025/10/06/resharper-and-rider-2025-2-3/
👉 @KodBlog
Узнать об изменениях в этой сборке и скачать обновление можно здесь:
https://blog.jetbrains.com/dotnet/2025/10/06/resharper-and-rider-2025-2-3/
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤2
Соглашения об именовании в C#
Согласны или нет?
Константы — в SCREAMING_CASE с подчёркиваниями
Поля экземпляра — в camelCase с префиксом подчёркивания
Статические поля — в PascalCase
Свойства — в PascalCase
Локальные переменные — в camelCase
👉 @KodBlog
Согласны или нет?
Константы — в SCREAMING_CASE с подчёркиваниями
Поля экземпляра — в camelCase с префиксом подчёркивания
Статические поля — в PascalCase
Свойства — в PascalCase
Локальные переменные — в camelCase
Please open Telegram to view this post
VIEW IN TELEGRAM
💯21🤯8❤4👍3
Улучшаем отладку EF Core с помощью тегов запросов
Отслеживание SQL-запросов, которые генерирует EF Core, порой похоже на поиск иголки в стоге сена. Когда ваше приложение создаёт десятки или сотни запросов, понять, какое LINQ-выражение породило конкретный SQL, становится настоящей головной болью. К счастью, EF Core предлагает удобный инструмент —> теги запросов.
Что такое теги запросов😜
Теги запросов позволяют добавлять пользовательские комментарии к SQL, создаваемому LINQ-выражениями. Эти комментарии отображаются прямо в сгенерированном SQL, что позволяет легко сопоставлять запрос с исходным кодом.
Чтобы добавить тег, используется метод TagWith для любого IQueryable:
Сгенерированный SQL будет выглядеть примерно так:
Благодаря тегу сразу видно, какой код породил запрос, без необходимости вручную сопоставлять SQL с LINQ.
Дополнительные возможности
Можно добавлять несколько тегов для большего контекста, а также включать значения переменных во время выполнения:
Результат:
*CorrelationId помогает проследить весь путь пользовательского запроса
Важные замечания
1. Влияние TagWith на производительность минимально, но не стоит злоупотреблять длинными тегами или сложной интерполяцией в горячих участках кода.
2. Теги должны быть строковыми литералами. Параметры напрямую они не принимают, но можно использовать интерполяцию строк или многострочные литералы.
👉 @KodBlog
Отслеживание SQL-запросов, которые генерирует EF Core, порой похоже на поиск иголки в стоге сена. Когда ваше приложение создаёт десятки или сотни запросов, понять, какое LINQ-выражение породило конкретный SQL, становится настоящей головной болью. К счастью, EF Core предлагает удобный инструмент —> теги запросов.
Что такое теги запросов
Теги запросов позволяют добавлять пользовательские комментарии к SQL, создаваемому LINQ-выражениями. Эти комментарии отображаются прямо в сгенерированном SQL, что позволяет легко сопоставлять запрос с исходным кодом.
Чтобы добавить тег, используется метод TagWith для любого IQueryable:
var orders = context.Orders
.TagWith("Заказы больше $1000")
.Where(o => o.Total > 1000)
.Include(o => o.Customer)
.ToList();
Сгенерированный SQL будет выглядеть примерно так:
-- Заказы больше $1000
SELECT [o].[Id], [o].[CustomerId], [o].[Total], [c].[Id], [c].[Name]
FROM [Orders] AS [o]
INNER JOIN [Customers] AS [c] ON [o].[CustomerId] = [c].[Id]
WHERE [o].[Total] > 1000.0
Благодаря тегу сразу видно, какой код породил запрос, без необходимости вручную сопоставлять SQL с LINQ.
Дополнительные возможности
Можно добавлять несколько тегов для большего контекста, а также включать значения переменных во время выполнения:
var userOrders = context.Orders
.TagWith("Запрос с дэшборда")
.TagWith($"User ID: {userId}")
.TagWith($"CorrelationId: {correlationId}")
.Where(o => o.CustomerId == userId)
.OrderByDescending(o => o.OrderDate)
.Take(10)
.ToList();
Результат:
-- Запрос с дэшборда
-- User ID: 12345
-- CorrelationId: 987654321
SELECT TOP(10) [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE [o].[CustomerId] = 12345
ORDER BY [o].[OrderDate] DESC
*CorrelationId помогает проследить весь путь пользовательского запроса
Важные замечания
1. Влияние TagWith на производительность минимально, но не стоит злоупотреблять длинными тегами или сложной интерполяцией в горячих участках кода.
2. Теги должны быть строковыми литералами. Параметры напрямую они не принимают, но можно использовать интерполяцию строк или многострочные литералы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21❤5🔥2
Два почти одинаковых SQL-запроса.
Один оказался быстрее в 451 раз.
Реализовывалась курсорная пагинация, и, казалось бы, логично —> добавить индекс, чтобы ускорить запрос.
Но именно здесь всё пошло не так.
Появился Index Scan по составному индексу и вроде бы отлично. Однако запрос стал медленнее, чем без индекса.
Почему так произошло?
Первая гипотеза — маленький датасет, и индекс просто не даёт ощутимого выигрыша. Но причина оказалась в другом.
Решение — использовать tuple comparison в SQL.
После этого индекс наконец заработал: 0.668 ms.
Оказалось, что оптимизатор запросов не всегда понимает, что составной индекс можно применить для построчного сравнения.
При tuple comparison индекс начинает использоваться корректно.
Без анализа плана выполнения запроса это было бы сложно заметить.
Полный разбор производительности — здесь
👉 @KodBlog
Один оказался быстрее в 451 раз.
Реализовывалась курсорная пагинация, и, казалось бы, логично —> добавить индекс, чтобы ускорить запрос.
Но именно здесь всё пошло не так.
Появился Index Scan по составному индексу и вроде бы отлично. Однако запрос стал медленнее, чем без индекса.
Почему так произошло?
Первая гипотеза — маленький датасет, и индекс просто не даёт ощутимого выигрыша. Но причина оказалась в другом.
Решение — использовать tuple comparison в SQL.
После этого индекс наконец заработал: 0.668 ms.
Оказалось, что оптимизатор запросов не всегда понимает, что составной индекс можно применить для построчного сравнения.
При tuple comparison индекс начинает использоваться корректно.
Без анализа плана выполнения запроса это было бы сложно заметить.
Полный разбор производительности — здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥5👏3👍1
Проверка на null, которая на самом деле null не проверяет 🚨
Рассмотрим такой код:
Мы сопоставляем массив строк с шаблоном (массив из одного элемента) и, если элемент не пустая строка, возвращаем его. Выглядит красиво и лаконично, правда?
Но на практике такой код может внезапно выдать
На SharpLab можно посмотреть три варианта такой проверки и понять, что происходит «под капотом»:
Оказывается, есть различие между шаблоном var (допускает null) и шаблоном не-var. Третий вариант — это шаблон не-null, который делает то же самое (но не требует явного указания типа). Обычно var используется вместо явного типа лишь для удобства чтения, но здесь у нас есть реальная разница в поведении.
👉 @KodBlog
Рассмотрим такой код:
string Test1(List<string> strs)
{
if(strs is [var s])
{
if (s.Length > 0)
return s;
}
return string.Join(",", strs);
}
Мы сопоставляем массив строк с шаблоном (массив из одного элемента) и, если элемент не пустая строка, возвращаем его. Выглядит красиво и лаконично, правда?
Но на практике такой код может внезапно выдать
NullReferenceExceptionНа SharpLab можно посмотреть три варианта такой проверки и понять, что происходит «под капотом»:
// if (text.Length > 0)
// нет проверки на null!!!
if(strs is [var s])
// if (text != null && text.Length > 0)
if(strs is [string s])
// if (text != null && text.Length > 0)
if(strs is [{} s])
Оказывается, есть различие между шаблоном var (допускает null) и шаблоном не-var. Третий вариант — это шаблон не-null, который делает то же самое (но не требует явного указания типа). Обычно var используется вместо явного типа лишь для удобства чтения, но здесь у нас есть реальная разница в поведении.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🌚6🍌2🌭1🍓1
Однофайловые скрипты в .NET
Dev Containers для изоляции и воспроизводимости окружения
.NET 10 Preview 7
Entity Framework Core
Dev Containers — это действительно удобно, особенно учитывая, сколько всего они могут настроить автоматически (например, SQL Server).
VS Code делает процесс настройки максимально простым.
Однофайловые скрипты поддерживаются только в VS Code и .NET CLI.
Подключение NuGet-пакетов:
Настройка свойств MSBuild:
Слева — C#-код однофайлового скрипта,
в центре — Dockerfile (из Dev Containers),
справа — конфигурация .devcontainer
👉 @KodBlog
Dev Containers для изоляции и воспроизводимости окружения
.NET 10 Preview 7
Entity Framework Core
Dev Containers — это действительно удобно, особенно учитывая, сколько всего они могут настроить автоматически (например, SQL Server).
VS Code делает процесс настройки максимально простым.
Однофайловые скрипты поддерживаются только в VS Code и .NET CLI.
Подключение NuGet-пакетов:
#:package <имя_пакета>@<версия>
Настройка свойств MSBuild:
#:property <имя_свойства>=<значение>
Слева — C#-код однофайлового скрипта,
в центре — Dockerfile (из Dev Containers),
справа — конфигурация .devcontainer
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🥴1
Псевдопеременные в отладчике Visual Studio для C#
В Visual Studio есть специальные псевдопеременные, которые показывают полезную информацию в окне Watch или Immediate. Их можно вводить как обычные переменные, но они не связаны с именами переменных в коде.
Основные псевдопеременные:
1. $exception
Оценивает текущее выброшенное исключение. Переменная доступна в момент выбрасывания исключения, а также в блоке catch, если исключение перехвачено. Это полезно в случаях, когда вы не добавили параметр Exception в предложение catch. Вы также можете увидеть исключение в окне Locals.
2. $returnvalue
Показывает и возвращает значение метода, из которого только что вышли. Это полезно, если вы не присваиваете возвращаемое значение какой-то переменной (а, например, сразу используете return). Заметьте, однако, что значение будет доступно только сразу после выхода из метода:
- на следующей строке, если вы поставили там точку останова, либо перескочили (F10) через вызов метода;
- при выходе из метода (на строке с закрывающей скобкой после return), если вы проходите по коду самого метода.
Кроме того, возвращаемое значение автоматически показывается в окнах Locals и Autos.
3. $user
Показывает информацию о текущем пользователе ОС и процессе. Сюда входит имя машины, привилегии процесса и некоторые другие сведения, связанные с безопасностью.
4. $threadSmallObjectHeapBytes
Отображает общее количество байт, выделенных в куче малых объектов (Small Object Heap) текущим потоком. (.NET 6+)
5. $threadUserOldHeapBytes
Отображает общее количество байт, выделенных в Пользовательской Куче Старшего поколения (User Old Heap) текущим потоком. User Old Heap = Large Object Heap + Pinned Object Heap (.NET 6+)
6. $1, $2, …, $n
Отображают по порядку объекты, для которых создан Object ID при отладке.
Эти псевдопеременные помогают быстро получать важные данные во время отладки без изменения кода.
Источник: тык
👉 @KodBlog
В Visual Studio есть специальные псевдопеременные, которые показывают полезную информацию в окне Watch или Immediate. Их можно вводить как обычные переменные, но они не связаны с именами переменных в коде.
Основные псевдопеременные:
1. $exception
Оценивает текущее выброшенное исключение. Переменная доступна в момент выбрасывания исключения, а также в блоке catch, если исключение перехвачено. Это полезно в случаях, когда вы не добавили параметр Exception в предложение catch. Вы также можете увидеть исключение в окне Locals.
2. $returnvalue
Показывает и возвращает значение метода, из которого только что вышли. Это полезно, если вы не присваиваете возвращаемое значение какой-то переменной (а, например, сразу используете return). Заметьте, однако, что значение будет доступно только сразу после выхода из метода:
- на следующей строке, если вы поставили там точку останова, либо перескочили (F10) через вызов метода;
- при выходе из метода (на строке с закрывающей скобкой после return), если вы проходите по коду самого метода.
Кроме того, возвращаемое значение автоматически показывается в окнах Locals и Autos.
3. $user
Показывает информацию о текущем пользователе ОС и процессе. Сюда входит имя машины, привилегии процесса и некоторые другие сведения, связанные с безопасностью.
4. $threadSmallObjectHeapBytes
Отображает общее количество байт, выделенных в куче малых объектов (Small Object Heap) текущим потоком. (.NET 6+)
5. $threadUserOldHeapBytes
Отображает общее количество байт, выделенных в Пользовательской Куче Старшего поколения (User Old Heap) текущим потоком. User Old Heap = Large Object Heap + Pinned Object Heap (.NET 6+)
6. $1, $2, …, $n
Отображают по порядку объекты, для которых создан Object ID при отладке.
Эти псевдопеременные помогают быстро получать важные данные во время отладки без изменения кода.
Источник: тык
Please open Telegram to view this post
VIEW IN TELEGRAM
Docs
Pseudovariables - Visual Studio (Windows)
Review pseudovariables in the Visual Studio debugger. Pseudovariables are terms used to display certain data in a variable window or the QuickWatch dialog box.
👍8❤3
В .NET 10 появился DATAS — механизм, который динамически подстраивает размер кучи под реальную нагрузку. Для Server GC это может заметно сократить потребление памяти, но в некоторых случаях снизить пропускную способность, поэтому стоит проверить метрики и при необходимости подстроить или отключить DATAS.
Механизм вычисляет Gen0-бюджет по Live Data Size (BCD), держит целевой throughput control point (по умолчанию 2%) и уменьшает кучу при снижении нагрузки. В статье описаны ограничения (одна куча на старте, влияние на startup и Gen2) и способы настройки.
Автор также разбирает формулу BCD (m ≈ 15000/√LDS), параметры GCDGen0GrowthPercent и GCDGen0GrowthMinFactor, и показывает, что включение DATAS снижает % pause time и общий размер кучи. Для диагностики доступны события SizeAdaptationTuning.
👉 @KodBlog
Механизм вычисляет Gen0-бюджет по Live Data Size (BCD), держит целевой throughput control point (по умолчанию 2%) и уменьшает кучу при снижении нагрузки. В статье описаны ограничения (одна куча на старте, влияние на startup и Gen2) и способы настройки.
Автор также разбирает формулу BCD (m ≈ 15000/√LDS), параметры GCDGen0GrowthPercent и GCDGen0GrowthMinFactor, и показывает, что включение DATAS снижает % pause time и общий размер кучи. Для диагностики доступны события SizeAdaptationTuning.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍2
Roslyn можно использовать просто как библиотеку для анализа и генерации кода. Например, можно написать консольное приложение, которое загрузит solution, найдет нужные паттерны и перепишет код. В то время как Roslyn Analyzers привязаны к одному проекту, сам Roslyn как библиотека позволяет анализировать весь solution целиком.
Создадим консольное приложение и добавим нужные пакеты NuGet:
Вот пример, как создать рабочее пространство Roslyn из решения:
Теперь можно работать с решением. Вот несколько примеров.
Анализируем синтаксические деревья:
Получаем все ссылки на символ:
Переименовываем символ
Обновляем документ с помощью DocumentEditor
Обновляем документ с помощью CSharpSyntaxRewriter:
Выберите любое «подопытное» решение, введите его путь вместо
👉 @KodBlog
Создадим консольное приложение и добавим нужные пакеты NuGet:
dotnet new console
dotnet add package Microsoft.Build.Locator
dotnet add package Microsoft.CodeAnalysis.CSharp
dotnet add package Microsoft.CodeAnalysis.CSharp.Workspaces
dotnet add package Microsoft.CodeAnalysis.Workspaces.Common
dotnet add package Microsoft.CodeAnalysis.Workspaces.MSBuild
Вот пример, как создать рабочее пространство Roslyn из решения:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;
// Находим сборки MSBuild в вашей системе
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();
// Возможно надо восстановить NuGet-пакеты решения перед тем, как открыть его в Roslyn.
// Зависимости могут потребоваться для корректного анализа.
// Создаём рабочее пространство Roslyn и загружаем решение
var ws = MSBuildWorkspace.Create();
var solution =
await ws.OpenSolutionAsync(@"my_solution.sln");
Теперь можно работать с решением. Вот несколько примеров.
Анализируем синтаксические деревья:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var syntree = await doc.GetSyntaxTreeAsync();
var semanticModel = await doc.GetSemanticModelAsync();
// Анализируем дерево
// Можно использовать CSharpSyntaxWalker для обхода
}
}
Получаем все ссылки на символ:
var comp =
await solution.Projects.First().GetCompilationAsync();
var symbol =
comp.GetSpecialType(SpecialType.System_String);
var refs =
await SymbolFinder.FindReferencesAsync(symbol, solution);
Переименовываем символ
var symbol = comp
.GetSymbolsWithName(s => s == "MyMethod")
.Single();
var newSolution = await Renamer
.RenameSymbolAsync(
solution,
symbol,
new SymbolRenameOptions()
{
RenameOverloads = true
},
"MyMethod2");
ws.TryApplyChanges(newSolution);
Обновляем документ с помощью DocumentEditor
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var editor =
await DocumentEditor.CreateAsync(doc);
// Изменяем
foreach(var emptyStatement in
editor
.OriginalRoot
.DescendantNodes()
.OfType<EmptyStatementSyntax>())
{
editor.RemoveNode(emptyStatement);
}
// Применяем изменения
var newDoc = editor.GetChangedDocument();
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}
Обновляем документ с помощью CSharpSyntaxRewriter:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
// Изменяем
var root = await doc.GetSyntaxRootAsync();
if(root is null)
continue;
var newRoot =
new CustomRewriter().Visit(root);
var newDoc =
doc.WithSyntaxRoot(newRoot);
// Применяем изменения
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}
internal sealed class CustomRewriter :
CSharpSyntaxRewriter
{
public override SyntaxNode?
VisitIfStatement(IfStatementSyntax node)
=> node.WithLeadingTrivia(
SyntaxFactory
.ParseLeadingTrivia("/* comment */"));
}
Выберите любое «подопытное» решение, введите его путь вместо
my_solution.sln, вставьте любой из примеров и можете пройти его в отладчике пошагово, чтобы посмотреть, как работает анализатор.Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤2🤔1