C# Portal | Программирование – Telegram
C# Portal | Программирование
14.9K subscribers
965 photos
117 videos
24 files
807 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для C#-разработчика

Связь: @devmangx

РКН: https://clck.ru/3FocB6
Download Telegram
Вышла онлайн IDE для .NET

Теперь приложения на C# и XAML можно писать прямо в браузере. 👨‍💻

IDE работает на WebAssembly, поддерживает drag-and-drop UI, сохранение проектов в облако или на ПК и компиляцию через Roslyn прямо в клиенте.

Запуск выполняется в отдельном iframe, чтобы интерфейс не подвисал, а в планах - автодополнение кода, AI-ассистент и шэринг проектов.

Попробовать можно на xaml.io

подробнее — Habr

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👎116👍5🤔2
Примеры полнотекстового поиска в Entity Framework:

Если ваша СУБД поддерживает эту возможность и вам действительно нужен поиск с подстановочными символами, рассмотрите использование полнотекстового поиска для более эффективных запросов.

// Полнотекстовый поиск по связанным терминам и словоформам  
// (например, "climate change", "climatic changes").
var resultsFreeText = context
.Articles
.Where(a => EF.Functions.FreeText(a.Content, "climate change")).ToList();


// Полнотекстовый поиск с операторами (AND, OR, NEAR) для точного совпадения фразы.
var resultsContains = context
.Articles
.Where(a => EF.Functions.Contains(a.Content, "climate change")).ToList();


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍65
Как на самом деле выполняется C#-код?

1. Вы пишете .cs-файл.

2. Компилятор (Roslyn) преобразует его в IL (Intermediate Language — промежуточный язык).

3. IL сохраняется в сборках (.dll / .exe).

Во время выполнения CLR :

- загружает сборки,

- проверяет IL на безопасность,

- компилирует «горячие» участки кода в машинные инструкции через JIT-компилятор.

CLR также обеспечивает сборку мусора, управление памятью и доступ к базовой библиотеке классов (System.*).

В итоге ваш C#-код работает как эффективный машинный код, сохраняя при этом типобезопасность, переносимость и удобство разработки.

Понимание этого пайплайна помогает при отладке проблем с производительностью, работе с зависимостями из NuGet и выборе между JIT- и AOT-компиляцией.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍255
This media is not supported in your browser
VIEW IN TELEGRAM
На GitHub есть репозиторий free-programming-books, где собрано более 4000 бесплатных книг, 2000 курсов и других полезных ресурсов по программированию

Для удобства поиска можно использовать этот инструмент

Этот проект - яркий пример силы опенсорс сообщества, который из клона списка со StackOverflow стал одним из самых популярных на GitHub ✌️

Русскоязычная версия ресурсов

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103
Одна из самых недооценённых фич в .NET — System.Threading.Channels.

С её помощью можно просто построить асинхронное взаимодействие через сообщения на C#. У Channel есть два API:

- для записи сообщений в канал,

- для асинхронного чтения сообщений из канала.

Можно запустить воркер в фоне, который будет забирать сообщения и передавать их на обработку. На этой основе легко собрать примитивный in-memory message bus (со всеми очевидными ограничениями).

Пример реализации тут

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍103
Как упаковать RPM на Linux через GitLab CI/CD?

В материале детально разбирается разработка службы на .NET Core, конфигурация spec-файла, применение макросов и запуск GitLab Runner внутри Docker. Полезные рекомендации для DevOps по автоматизации билда и контролю версий.

Читать подробнее: https://habr.com/ru/articles/952748/

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍2
Чистим NuGet и освобождаем десятки гигабайт

Со временем 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


👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25👍75
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍125🤣3🔥2
Одна из частых проблем у многих программистов — они используют async и await, не понимая, что на самом деле происходит.

Асинхронная задача не блокирует основной поток, поэтому пока выполняется длительная операция, мы можем выполнять другие действия.

Console.WriteLine("Запуск программы");

// начинаем загрузку файла, длительность — 3 секунды
var downloadFileTask = DownloadFile();

// основной поток не блокируется — можем выполнять другие задачи
Console.WriteLine("Делаем другие вещи: обращаемся к БД, отправляем письма и т.д.");

// на этом этапе ожидаем завершения загрузки файла
await downloadFileTask;

Console.WriteLine("Программа завершена");

async Task DownloadFile()
{
Console.WriteLine("Загружаем файл...");
await Task.Delay(3000); // симулируем ожидание 3 секунды
Console.WriteLine("Файл загружен!");
}


👉 @KodBlog
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍73🤔1
Если у нас есть List<T> и IList<T>, то перебор элементов через List<T> выполняется быстрее, чем через IList<T>. Почему так происходит?

[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> этого делать не нужно — тип уже известен, и вызов идёт напрямую, без дополнительной диспетчеризации.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍11🤯6🥴1
Получение информации о .NET Runtime в приложении

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
❤‍🔥9👍43
Этот пост вряд ли принесёт вам практическую пользу в повседневной жизни, ну, разве что улыбку. Давайте доведём ValueTuple до крайности ⌨️

Тип 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

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
🤯114👍4😁1
Rider 2025.2.3 был выпущен

Узнать об изменениях в этой сборке и скачать обновление можно здесь:

https://blog.jetbrains.com/dotnet/2025/10/06/resharper-and-rider-2025-2-3/

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72
Соглашения об именовании в C#

Согласны или нет?

Константы — в SCREAMING_CASE с подчёркиваниями

Поля экземпляра — в camelCase с префиксом подчёркивания

Статические поля — в PascalCase

Свойства — в PascalCase

Локальные переменные — в camelCase

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
💯21🤯84👍3
Улучшаем отладку EF Core с помощью тегов запросов

Отслеживание 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. Теги должны быть строковыми литералами. Параметры напрямую они не принимают, но можно использовать интерполяцию строк или многострочные литералы.

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
👍215🔥2
Два почти одинаковых SQL-запроса.

Один оказался быстрее в 451 раз.

Реализовывалась курсорная пагинация, и, казалось бы, логично —> добавить индекс, чтобы ускорить запрос.

Но именно здесь всё пошло не так.

Появился Index Scan по составному индексу и вроде бы отлично. Однако запрос стал медленнее, чем без индекса.

Почему так произошло?

Первая гипотеза — маленький датасет, и индекс просто не даёт ощутимого выигрыша. Но причина оказалась в другом.

Решение — использовать tuple comparison в SQL.

После этого индекс наконец заработал: 0.668 ms.

Оказалось, что оптимизатор запросов не всегда понимает, что составной индекс можно применить для построчного сравнения.

При tuple comparison индекс начинает использоваться корректно.

Без анализа плана выполнения запроса это было бы сложно заметить.

Полный разбор производительности — здесь

👉 @KodBlog
Please open Telegram to view this post
VIEW IN TELEGRAM
10🔥5👏3👍1
Проверка на null, которая на самом деле null не проверяет 🚨

Рассмотрим такой код:

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 используется вместо явного типа лишь для удобства чтения, но здесь у нас есть реальная разница в поведении.

👉 @KodBlog
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-пакетов:

#:package <имя_пакета>@<версия>


Настройка свойств MSBuild:

#:property <имя_свойства>=<значение>


Слева — C#-код однофайлового скрипта,
в центре — Dockerfile (из Dev Containers),
справа — конфигурация .devcontainer

👉 @KodBlog
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83