Я отвечал на вопрос о возможности закрытия (законно) продления времени жизни объектов, когда столкнулся с каким-то чрезвычайно любопытным генератором кода со стороны компилятора C # (4.0, если это имеет значение).
Самое короткое повторение, которое я могу найти, следующее:
- Создайте лямбду, которая захватывает локальный при вызове статического метода содержащего типа.
- Назначьте созданную ссылку на делегат полю экземпляра содержащего объекта.
Результат: компилятор создает объект замыкания, который ссылается на объект, который создал лямбду, когда у него нет причин для этого - «внутренняя» цель делегата является статическим методом, и членам экземпляра объекта-лямбда-объекта не нужно быть (и не) тронут, когда делегат выполняется. По сути, компилятор действует так, как запрограммировал программист this
без причины.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
Сгенерированный код из сборки выпуска (декомпилированной в «более простой» C #) выглядит следующим образом:
public void InstanceMethod()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.<>4__this = this; // What's this doing here?
CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public Foo <>4__this; // Never read, only written to.
public double capturedVariable;
// Methods
public void <InstanceMethod>b__0()
{
Foo.StaticMethod(this.capturedVariable);
}
}
Заметьте, что <>4__this
поле объекта замыкания заполнено ссылкой на объект, но никогда не читается из (нет причин).
Так что здесь происходит? Позволяет ли это спецификация языка? Это ошибка / странность компилятора или есть веская причина (которую я явно упускаю) для замыкания для ссылки на объект? Это вызывает у меня беспокойство, потому что это похоже на рецепт для программистов, удовлетворяющих замыканиям (таких как я), невольно вводить странные утечки памяти (представьте, если делегат использовался в качестве обработчика событий) в программах.
this
.Ответы:
Это конечно похоже на ошибку. Спасибо, что обратили на это мое внимание. Я посмотрю на это. Возможно, он уже найден и исправлен.
источник
Кажется, это ошибка или ненужное:
Я запускаю тебя в качестве примера в IL lang:
Пример 2:
в cl: (примечание !! теперь эта ссылка исчезла!)
Пример 3:
в IL: (этот указатель вернулся)
И во всех трех случаях метод -b__0 () - выглядит одинаково:
И во всех трех случаях есть ссылка на статический метод, поэтому он делает его более странным. Так что после этого небольшого анализа я скажу, что это ошибка / ничего хорошего. !
источник
Foo.InstanceMethod
сделано статичным, это также удалит ссылку? Я был бы благодарен, чтобы знать.Foo.InstanceMethod
были статичными, не было бы видно ни одного экземпляра, и, следовательно, не было бы никакого способаthis
быть захваченным закрытием.