VulCode
Blog

ILSpy: dekompilator nie zostawia już pola pamięci podręcznej delegata

Dla kodu _ = (Action<int>)M; ILSpy zwracał wynik z polem <>O.<0>__M, którego nie da się skompilować. Teraz w takim miejscu pojawia się zwykłe utworzenie delegata.

DualFroz - VulCode CEODualFroz - VulCode CEO1 min czytania
ILSpy: dekompilator nie zostawia już pola pamięci podręcznej delegata - DualFroz - VulCode CEO
Tytuł zmiany w repozytorium
Remove the delegate cache left by a discarded method-group conversion
Scalono
19.09.2026
Opisano tutaj
6.10.2026

Wpis powstał 17 dni po scaleniu zmiany.

ILSpy
Technologie:C#.NETIL

Co nie działało

Kompilator C# zapisuje delegat utworzony z grupy metod w statycznym polu pamięci podręcznej, żeby nie tworzyć go od nowa przy każdym wywołaniu. ILSpy, otwarty dekompilator dla .NET, rozpoznaje ten wzorzec i zamienia go z powrotem na czytelny zapis. Transformacja zakładała jednak, że pole z pamięci zostanie odczytane dokładnie raz.

Przy kodzie _ = (Action<int>)M; delegat jest tworzony i od razu odrzucany, więc pole nie jest odczytywane ani razu. Wzorzec nie pasował, a w wyniku dekompilacji zostawało pole o nazwie <>O.<0>__M, której nie da się użyć w C#. Taki kod się nie kompiluje (zgłoszenie #3965).

Co zmieniliśmy

Gdy pole pamięci nie ma żadnego odczytu, sprawdzenie pamięci zostaje zastąpione utworzeniem delegata, które zabezpieczało. W wyniku pojawia się new Action<int>(M);, czyli kod, który się kompiluje i robi to samo.

Jak to sprawdziliśmy

Doszedł zestaw przypadków testowych Issue3965 z dziewięcioma scenariuszami: pojedyncza odrzucona konwersja, konwersja przed użyciem i po użyciu tej samej metody, kilka konwersji pod rząd, dwie do tej samej metody, konwersja w warunku i w pętli. Każdy ma zapisany oczekiwany wynik, z którym test porównuje dekompilację.

Efekt

Zmianę scalono 19 września 2026. W ILSpy przyjęto też drugą naszą zmianę (#4134), która rozpoznaje test typu w filtrze wyjątków z Visual Basica.

Ten sam przypadek przed zmianą i po niej

Na przykładzie z wpisu: co wychodziło przed poprawką i co wychodzi teraz.

Kod w C#, który dekompilujemy
_ = (Action<int>)M;

Delegat tworzony z grupy metod i od razu odrzucany, więc pole pamięci podręcznej nie jest odczytywane ani razu.

Przed zmianą
<>O.<0>__M

W wyniku zostawało pole pamięci podręcznej o nazwie, której nie da się użyć w C#. Kod się nie kompilował (zgłoszenie #3965).

Po zmianie
new Action<int>(M);

Zwykłe utworzenie delegata: kompiluje się i robi to samo.

Czego nas to nauczyło

Dekompilator musi rozpoznać wzorce, które generuje kompilator, inaczej zostawia w kodzie śmieci po optymalizacjach. Każda poprawka wymagała przeczytania, co dokładnie emituje kompilator C# i VB.

Z opisu zmiany w pull requeście #4133. Przypadki testowe Issue3965 mają dziewięć scenariuszy z zapisanym oczekiwanym wynikiem.

Pytania o zmianę #4133

Gdzie jest zmiana, jak ją sprawdziliśmy i kto ją przyjął.