Odpowiadałem na pytanie o możliwość domknięć (uzasadnionych) wydłużania czasu życia obiektów, kiedy natknąłem się na jakiś niezwykle ciekawy gen kodu ze strony kompilatora C # (4.0, jeśli to ma znaczenie).
Najkrótsza reprodukcja, jaką mogę znaleźć, jest następująca:
- Utwórz lambdę, która przechwytuje lokalną podczas wywoływania metody statycznej typu zawierającego.
- Przypisz wygenerowane odwołanie do delegata do pola wystąpienia obiektu zawierającego.
Wynik: kompilator tworzy obiekt zamknięcia, który odwołuje się do obiektu, który utworzył lambdę, gdy nie ma powodu - „wewnętrznym” celem delegata jest metoda statyczna , a elementy składowe instancji obiektu tworzącego lambdę nie muszą być (i nie są) dotykane podczas wykonywania delegata. W rzeczywistości kompilator zachowuje się tak, jak programista przechwycił this
bez powodu.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
Wygenerowany kod z kompilacji wydania (zdekompilowany do „prostszego” C #) wygląda następująco:
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);
}
}
Zauważ, że <>4__this
pole obiektu zamknięcia jest wypełnione odwołaniem do obiektu, ale nigdy nie jest odczytywane (nie ma powodu).
Więc co się tutaj dzieje? Czy specyfikacja języka na to pozwala? Czy jest to błąd / dziwność kompilatora, czy też istnieje dobry powód (którego wyraźnie mi brakuje), aby zamknięcie odwoływało się do obiektu? To mnie niepokoi, ponieważ wygląda to na przepis na zadowolonych z zamknięcia programistów (takich jak ja) na nieświadome wprowadzanie dziwnych wycieków pamięci (wyobraź sobie, że delegat był używany jako program obsługi zdarzeń) do programów.
this
.Odpowiedzi:
To z pewnością wygląda na błąd. Dziękuję za zwrócenie mi na to uwagi. Przyjrzę się temu. Możliwe, że został już znaleziony i naprawiony.
źródło
Wydaje się, że to błąd lub niepotrzebne:
Uruchomię ci przykład w języku IL lang:
Przykład 2:
in cl: (Uwaga! teraz to odniesienie zniknęło!)
Przykład 3:
w IL: (Ten wskaźnik powrócił)
We wszystkich trzech przypadkach metoda-b__0 () - wygląda tak samo:
We wszystkich 3 przypadkach istnieje odniesienie do metody statycznej, co czyni ją bardziej dziwną. Więc po tej małej analizie powiem, że to błąd / na nic. !
źródło
Foo.InstanceMethod
jest statyczny, czy spowoduje to również usunięcie odniesienia? Byłbym wdzięczny, gdyby wiedział.Foo.InstanceMethod
były również statyczne, nie byłoby widać żadnej instancji, a zatem niethis
można by jej uchwycić przez zamknięcie.