C# Ready | Unity
10.2K subscribers
1.44K photos
84 videos
714 links
Авторский канал по разработке на C# и Unity.
Ресурсы, гайды, задачи, шпаргалки.
Информация ежедневно пополняется!

Cотрудничество: @energy_c

РКН: https://clck.ru/3SBaT3
Download Telegram
Почему нельзя удалять элементы из List прямо внутри foreach?

foreach в C# удобен для чтения коллекции, но в нём лучше не менять коллекцию во время обхода.

На первый взгляд такой код выглядит нормально:
foreach (var user in users)
{
if (!user.Active) users.Remove(user);
}


Но во время выполнения List меняется, а перечислитель foreach ожидает, что структура коллекции останется прежней.

В результате можно получить InvalidOperationException:
Collection was modified;
enumeration operation may not execute.


Для простого удаления по условию чаще всего лучше использовать RemoveAll:
users.RemoveAll(user => !user.Active);


Мы не вручную управляем индексами, а говорим списку удалить все элементы, которые подходят под условие.

Если нужна более сложная логика, можно идти с конца по индексам:
for (int i = users.Count - 1; i >= 0; i--)
{
if (!users[i].Active) users.RemoveAt(i);
}


Обход с конца важен, потому что удаление элемента сдвигает следующие элементы влево. При движении назад это не ломает ещё не проверенную часть списка.

Ещё один вариант подходит, когда исходный список лучше не трогать:
users = users
.Where(user => user.Active)
.ToList();


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

foreach используют для чтения, а для удаления выбирают RemoveAll, обратный for или создание нового списка.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🔥4
Почему record в C# не делает объект полностью неизменяемым?

record часто воспринимают как “immutable-класс”:
public record User(string Name, List<string> Roles);


Но неизменяемым становится не всё содержимое объекта.

Свойство Roles нельзя просто заменить, если оно init-only:
var user = new User("Ann", new List<string>());


Но сам список внутри всё ещё изменяемый:
user.Roles.Add("admin");


То есть record защищает ссылку на список, но не защищает сам список от изменений.

Это особенно важно, если объект передаётся между слоями приложения:
public record Profile(string Name, List<string> Permissions);


Код снаружи может сохранить ссылку и позже поменять содержимое:
var roles = new List<string> { "user" };
var profile = new Profile("Ann", roles);

roles.Add("admin");


Если нужен более безопасный вариант, лучше принимать IReadOnlyList:
public record Profile(
string Name,
IReadOnlyList<string> Permissions
);


А внутри создавать копию:
var profile = new Profile(
"Ann",
roles.ToArray()
);


Для публичных моделей это часто снижает риск случайных мутаций.

record даёт удобное сравнение и init-свойства, но не делает вложенные коллекции immutable. Изменяемые поля всё равно нужно контролировать отдельно.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
❤️ dotnet-learning — большая база ресурсов для изучения .NET!

Здесь собраны полезные материалы для обучения, практики и более глубокого изучения подходов разработки на .NET. Репозиторий помогает не просто изучать отдельные технологии, а выстраивать целостное понимание платформы. Такой формат удобен и новичкам, и разработчикам с опытом, которые хотят структурировать знания или закрыть пробелы.

Оставляю ссылочку: GitHub 📱


➡️ C# Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤6🔥4
👍7❤5🔥5
Шпаргалка по DateTime и форматам дат в C#!

Например, yyyy отвечает за год, MM за месяц, dd за день, HH за часы в 24-часовом формате, а zzz показывает смещение часового пояса.

На картинке собраны частые преобразования дат и времени. Есть Parse, ParseExact, ToString с форматами, Unix timestamp, DateTimeOffset и примеры конвертации между строками, DateTime и DateTimeOffset.

Сохрани, чтобы не потерять!

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3🔥3👎2
Разбираем File и Path 7 инструментов для работы с файлами!

В C# для повседневной работы с файлами чаще всего хватает стандартных классов File, Directory и Path. Они закрывают проверку существования, чтение, запись, создание папок, обход директорий и безопасную сборку путей.

В этой шпоре собраны File.Exists, ReadAllText, WriteAllText, Directory.CreateDirectory, Directory.EnumerateFiles, Path.Combine и Path.GetExtension.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤3🤝3👍1
Почему Count() в LINQ может сделать лишнюю работу?

В C# часто нужно проверить, есть ли в последовательности хотя бы один элемент. Иногда для этого пишут так:
if (items.Count() > 0)
{
Console.WriteLine("not empty");
}


Для List<T> это может выглядеть безобидно, но LINQ работает не только со списками. Если items является IEnumerable<T>, Count() может пройти всю последовательность до конца.

На ленивых источниках это особенно неприятно:
var items = GetRows()
.Where(row => row.IsActive);


Проверка через Count() заставит перебрать все подходящие элементы, хотя для ответа достаточно первого.

Когда нужен только факт наличия элемента, лучше использовать Any():
if (items.Any())
{
Console.WriteLine("not empty");
}


Any() останавливается сразу после первого найденного элемента. Это проще по смыслу и часто дешевле по выполнению.

Если коллекция уже лежит в памяти как List<T>, массив или HashSet<T>, можно использовать свойство:
if (users.Count > 0)
{
Send(users);
}


А если действительно нужно точное количество, тогда Count() уместен:
var activeCount = users.Count(u => u.IsActive);


Для вопроса есть ли что-то используй Any(), для точного числа используй Count(), а для готовых коллекций смотри на свойство Count или Length.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤5🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
🐱 C# Job Questions — большая база для подготовки к собеседованиям!

Здесь собраны вопросы с развернутыми ответами и примерами кода по C# и .NET, ООП, алгоритмам и структурам данных, архитектуре, базам данных и многопоточности. Дополнительная тренировка, чтобы системно повторить теорию, проверить пробелы и подготовиться к технической части собесов.

Оставляю ссылочку: GitHub 📱


➡️ C# Ready | #репозиторий
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤4🔥3
Почему ToLower() перед сравнением строк может быть лишним?

Когда нужно сравнить две строки без учёта регистра, часто сначала приводят обе к нижнему регистру. Код работает, но создаёт новые строки и может зависеть от текущей культуры.
if (input.ToLower() == "admin")
{
GrantAccess();
}


Для имён ролей, HTTP-заголовков, ключей конфигурации и технических идентификаторов обычно нужен предсказуемый ordinal-сравнитель.
if (string.Equals(input, "admin",
StringComparison.OrdinalIgnoreCase))
{
GrantAccess();
}


Этот вариант не создаёт две временные строки и сразу показывает намерение. Он сравнивает символы без правил конкретного языка пользователя.

Особенно важно не использовать ToLower или ToUpper при проверке ключей и значений.
bool json = contentType.StartsWith(
"application/json", StringComparison.OrdinalIgnoreCase);


Для отображаемого пользователю текста иногда действительно нужен CurrentCultureIgnoreCase. Но для кода и протоколов чаще всего выбирают OrdinalIgnoreCase.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥5❤4👎1
Как в C# не забыть про ConfigureAwait(false)?

Когда пишешь библиотечный async-код, часто встречается такой вариант:
var json = await httpClient.GetStringAsync(url);
return JsonSerializer.Deserialize<User>(json);


Для приложения это обычно нормально. Но для библиотек и низкоуровневого кода часто лучше явно сказать, после await не нужно возвращаться в исходный context.

Для этого используют ConfigureAwait(false):
var json = await httpClient
.GetStringAsync(url)
.ConfigureAwait(false);


После await продолжение может выполниться на любом подходящем потоке:
return JsonSerializer.Deserialize<User>(json);


Это особенно полезно в shared-библиотеках, SDK, инфраструктурном коде и клиентах к внешним API.

Например:
public async Task<User> LoadUserAsync(string id)
{
var response = await client.GetAsync($"/users/{id}")
.ConfigureAwait(false);

return await ParseAsync(response)
.ConfigureAwait(false);
}


Но в UI-коде всё иначе. Если после await нужно обновить интерфейс, context может быть нужен:
label.Text = "Loaded";


В таком случае ConfigureAwait(false) может увести продолжение с UI-потока.

➡️ C# Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥5👍3
Blazor против Vue.js глазами C# разработчика!

Автор сравнивает два frontend-подхода на опыте реальных проектов. Статья показывает, что будет привычным, а что неожиданным для разработчика, который приходит в интерфейсы из C# и .NET.

В статье разбирают:
• как ощущается компонентная разработка в Blazor и Vue
• где C# стек даёт сильную сторону на frontend
• какие различия важнее синтаксиса и шаблонов

Продолжай читать на Habr


➡️ C# Ready | #статья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3🔥3👎1
Шпаргалка по поколениям сборщика мусора в .NET!

На картинке показаны поколения управляемой кучи, продвижение выживших объектов и три основные фазы GC. Mark находит достижимые объекты, sweep освобождает мусор, а compact уменьшает фрагментацию памяти.

Например, Gen 0 хранит новые короткоживущие объекты, Gen 1 служит промежуточной зоной, а Gen 2 содержит долгоживущие данные. Объекты крупнее 85 КБ обычно попадают в Large Object Heap.

Сохрани, чтобы не потерять!

➡️ C# Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥4