Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин
foreach (var i in 435972)
{
Console.WriteLine(i);
}
Конечно, если мы просто так это используем, компилятор ругнётся. Наша задача в том, чтобы это исправить. И это можно легко сделать с помощью методов расширения.
Видите ли, foreach не требует от типа реализации интерфейса вроде IEnumerable или IEnumerator. Компилятору нужен всего лишь метод GetEnumerator, возвращающий объект итератора (подробнее про паттерн Итератор), в котором есть свойство Current и метод MoveNext. Давайте добавим его:
public static class Extensions
{
public static IntEnumerator
GetEnumerator(this int i) => new(i);
}
public struct IntEnumerator
{
private readonly List<int> list;
private int idx = -1;
public IntEnumerator(int num)
{
list = new List<int>();
while(num > 0)
{
list.Add(num % 10);
num /= 10;
}
list.Reverse();
}
public int Current => list[idx];
public bool MoveNext()
{
idx++;
return list.Count > idx;
}
}
Ничего сложного. Мы «добавляем» в тип int метод GetEnumerator, который возвращает IntEnumerator. IntEnumerator выполняет требования для цикла foreach. Этот итератор просто помещает отдельные цифры целого числа в список и возвращает их одну за другой по требованию.
В результате наш код скомпилируется и выведет:
4 3 5 9 7 2Источник: https://steven-giesel.com/blogPost/5360d1c3-89f6-4a08-9ee3-6ddbe1b44236
public record SomeRecord(string Val)
{
public string CalcVal { get; } =
Val + " *calculated*";
}
Мы используем первичный конструктор для объявления и инициализации свойства Val, но у нас также есть другое свойство CalcVal, которое вычисляется с использованием Val. Вроде просто. Но что если мы используем with для создания другой записи:
var x = new SomeRecord("42");
var y = x with {Val = "69"};
Console.WriteLine(x);
Console.WriteLine(y);
Вот что получилось:
SomeRecord { Val = 42, CalcVal = 42 *calculated* }
SomeRecord { Val = 69, CalcVal = 42 *calculated* }
CalcVal во второй записи по-прежнему имеет то же значение, что и в первой. Это имеет смысл, так как при использовании выражения with запись клонируется, а новые значения получают только свойства, указанные в скобках.
Простой способ исправить это — использовать вычисляемое свойство:
public record SomeRecord(string Val)
{
public string CalcVal => Val + " *calculated*";
}
Это решает проблему и может быть допустимым решением в целом. Однако каждый раз, когда мы вызываем CalcVal значение будет вычисляться заново. В зависимости от того, как используется свойство, это может быть, а может и не быть проблемой, поскольку мы всегда повторяем одну и ту же логику и создаем новые объекты для возврата значения.
Источник: https://blog.codingmilitia.com/2022/09/01/beware-of-records-with-expressions-and-calculated-properties/netdeveloper2022JRGpc дает скидку от 20% на билеты из категории «Для частных лиц».
Подробности и билеты на сайте — dotnext.ruclass MyClass
{
// Блокировка защищает поле _value.
private readonly object _mutex = new object();
private int _value;
public void Increment()
{
lock (_mutex)
{
_value = _value + 1;
}
}
}
В .NET существует несколько механизмов блокировки: Monitor, Spin, Lock и ReaderWriterLockSlim. В большинстве приложений эти типы блокировок практически никогда не должны использоваться напрямую. В частности, разработчики часто используют ReaderWriterLockSlim, даже когда такая сложность не является необходимой. Базовая команда lock нормально справляется с 99% случаев.
При использовании блокировок следует руководствоваться четырьмя важными рекомендациями:
1. Ограничьте видимость блокировки.
Объект, используемый в команде lock, должен быть приватным полем, которое никогда не должно быть доступным извне класса. Обычно есть не более одного поля блокировки на тип; если у вас их несколько, рассмотрите возможность рефакторинга этого типа на несколько типов. Блокировка может устанавливаться по любому ссылочному типу, но рекомендуется создавать отдельное поле специально для команды lock, как в примере выше. Если вы устанавливаете блокировку по другому экземпляру, убедитесь в том, что он является приватным для вашего класса; он не должен передаваться в конструкторе или возвращаться из get-метода свойства. Никогда не используйте lock(this) или lock с любым экземпляром Type или string; это может привести к взаимоблокировкам, доступным из другого кода.
2. Оставьте комментарий, что именно защищает блокировка.
Об этом шаге легко забыть во время первоначального написания кода, но когда кодовая база вырастет, это может оказаться важным.
3. Сократите до минимума объем кода, защищённого блокировкой.
Один из аспектов, на которые следует обращать внимание, — блокирующие вызовы при удержании блокировок. В идеале их быть вообще не должно.
4. Никогда не выполняйте произвольный код при удержании блокировки.
Это включает выдачу событий, вызов виртуальных методов или вызов делегатов. Если вам потребуется выполнить произвольный код, сделайте это после снятия блокировки.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 12.