Comments 14
IL код:
Это не IL-код.
Надо было типы назвать сообветствующе, чтобы понимать где класс, где структура из кода.
И лучше определится с heap, хип, куча и писать везде одинаково.
Хорошая статья чтобы освежить память, но с неймингом и правда беда. Правда в последней части лично я бы выразился не "копия структуры не создаётся", а структура боксится 2 раза в 2 разных ссылочных объекта. И я бы добавил довольно очевидные, но полезные для подрастающих разработчиков вещи, что low-level c# это пережёванный компилятором код, поэтому лапша
Action act = (Action) Delegate.Combine((Delegate) new Action((object) cnter, __methodptr(Iteration)), (Delegate) new Action((object) cnter, __methodptr(Iteration)));
На деле оптимизация компилятора над Action
act = cnter.Iteration;
+ заворачиванием правой части выражения act += cnter.Iteration
в Action-делегат и итоговой комбинацией двух получившихся делегатов. Здесь же можно добавить, что любая операция, происходящая между делегатом и методом под капотом всегда приводит к оборачиванию метода экземпляром делегата, включая подписку на event, если только в событии не переопределены аксессоры add/remove.
Ну и просто любопытные вещи в духе myDelegate()
полностью эквивалентен myDelegate.Invoke()
, с той лишь разницей что во второй вариант можно вставить Null-conditional myDelegate?.Invoke()
, про очень полезный паттерн применения IDisposable и комбинации(подписки) делегатов и то, почему он защищает от утечек памяти (статья очень хорошо акцентирует внимание на захвате ссылки на экземпляр объекта), хотя про события я бы в целом добавил, материала не так много, при этом с моего опыта именно через инкапсулирующие event`ы разработчики чаще всего работают с делегатами на практике, ну и всякая вариантность generic-делегатов, хотя это уже довольно общая информация.
В любом случае автору плюсик.
Автору статьи загадка
Func<int> x = () => 1;
x += () => 2;
Console.WriteLine(x());
Какой будет результат, не запуская код?
Без этого статья будет не полной.
Ну и, как обычно, про отличающееся поведение замыкания для переменной цикла foreach все забыли.
Да кому нужно изменение поведения в netfx40 15-летней давности... (кроме тех, кто поддерживает такой код).
И да, надо сказать, что код, написанный в старом стиле (копия переменной цикла в локальную), прекрасно работает.
А я не писал про изменение поведения. Я писал про отличие поведения переменных в циклах for и foreach. Вещь совершенно неочевидная.
Ну и на самом деле легаси кода предостаточно. Работал в весьма крупной страховой компании, так там основной софт, на котором крутятся все основные процессы написан на 4 фреймворке. И когда замену этому напишут, совершенно непонятно. И, конечно же, это все приходится поддерживать.
В этом случае стоит только запомнить, что сейчас форич возвращает нам каждый раз новую переменную, в отличии от for. Поэтому под капотом именно в цикле форич будет создаваться класс, метод которого мы сделали как лямбду. И каждый экземпляр этого класса ссылается на свое значение.
try
{
while (enumerator.MoveNext())
{
Program.<>c__DisplayClass0_0 cDisplayClass00 = new Program.<>c__DisplayClass0_0();
cDisplayClass00.i = enumerator.Current;
action = (Action) Delegate.Combine((Delegate) action, (Delegate) new Action((object) cDisplayClass00, __methodptr(<Main>b__0)));
}
}
finally
{
enumerator.Dispose();
}
Вещь неочивидная, но тут только стоит помнить о переменной в фориче, как она возвращается + во что раскроется лямбда.
Делегаты в C# подробнее