Gdzie dowiedzieć się o „magicznych nazwach” debuggera VS

110

Jeśli kiedykolwiek korzystałeś z Reflectora, prawdopodobnie zauważyłeś, że kompilator C # generuje typy, metody, pola i zmienne lokalne, które zasługują na „specjalne” wyświetlanie przez debuger. Na przykład zmienne lokalne zaczynające się od „CS $” nie są wyświetlane użytkownikowi. Istnieją inne specjalne konwencje nazewnictwa dla typów zamykania metod anonimowych, pól zapasowych właściwości automatycznych i tak dalej.

Moje pytanie: gdzie się dowiedzieć o tych konwencjach nazewnictwa? Czy ktoś wie o jakiejś dokumentacji?

Moim celem jest, aby PostSharp 2.0 używał tych samych konwencji.

Gael Fraiteur
źródło

Odpowiedzi:

209

Są to nieudokumentowane szczegóły implementacji kompilatora i mogą ulec zmianie w dowolnym momencie. (AKTUALIZACJA: Zobacz GeneratedNames.cs źródła C # dla bieżących szczegółów; poniższy opis jest nieco nieaktualny).

Ponieważ jednak jestem miłym facetem, oto niektóre z tych szczegółów:

Jeśli masz nieużywaną zmienną lokalną, którą optymalizator usuwa, i tak wyślemy dla niej informacje debugowania do PDB. Umieściliśmy sufiks __Deleted$na takich zmiennych, aby debugger wiedział, że znajdują się one w kodzie źródłowym, ale nie są reprezentowane w pliku binarnym.

Miejsca na zmienne tymczasowe przydzielane przez kompilator otrzymują nazwy według wzoru CS $ X $ Y, gdzie X to „rodzaj tymczasowy”, a Y to liczba przydzielonych do tej pory tymczasowych elementów. Tymczasowe rodzaje to:

0 --> short lived temporaries
1 --> return value temporaries
2 --> temporaries generated for lock statements
3 --> temporaries generated for using statements
4 --> durable temporaries
5 --> the result of get enumerator in a foreach
6 --> the array storage in a foreach
7 --> the array index storage in a foreach.  

Rodzaje tymczasowe od 8 do 264 to dodatkowe magazyny indeksów tablic dla tablic wielowymiarowych.

Rodzaje tymczasowe powyżej 264 są używane do tymczasowych obejmujących ustaloną instrukcję ustalającą ciąg.

Specjalne nazwy generowane przez kompilator są generowane dla:

1 --> the iterator state ("state")
2 --> the value of current in an iterator ("current")
3 --> a saved parameter in an iterator
4 --> a hoisted 'this' in an iterator ("this")
5 --> a hoisted local in an iterator
6 --> the hoisted locals from an outer scope
7 --> a hoisted wrapped value ("wrap")
8 --> the closure class instance ("locals")
9 --> the cached delegate instance ("CachedAnonymousMethodDelegate")
a --> the iterator instance ("iterator")
b --> an anonymous method
c --> anonymous method closure class ("DisplayClass")
d --> iterator class
e --> fixed buffer struct ("FixedBuffer")
f --> anonymous type ("AnonymousType")
g --> initializer local ("initLocal")
h --> query expression temporary ("TransparentIdentifier")
i --> anonymous type field ("Field")
j --> anonymous type type parameter ("TPar")
k --> auto prop field ("BackingField")
l --> iterator thread id
m --> iterator finally ("Finally")
n --> fabricated method ("FabricatedMethod")
o --> dynamic container class ("SiteContainer")
p --> dynamic call site ("Site")
q --> dynamic delegate ("SiteDelegate")
r --> com ref call local ("ComRefCallLocal")
s --> lock taken local ("LockTaken")

Wzorzec generowania magicznych imion to: P<N>C__SIgdzie:

  • P to CS $ dla delegatów w pamięci podręcznej i instancji klas wyświetlania, w przeciwnym razie puste.
  • N to oryginalna nazwa związana z rzeczą, jeśli istnieje
  • C to znak od 1 do s wymieniony powyżej
  • S jest przyrostkiem opisowym („bieżący”, „stan” itd.), Dzięki czemu nie trzeba zapamiętywać powyższej tabeli podczas czytania metadanych.
  • I jest opcjonalnym unikalnym numerem
Eric Lippert
źródło
2
Dziękuję Ci! Zobaczę, czy uda mi się sprawić, by klasy zamknięcia PostSharp zachowywały się tak dobrze, jak to, co generuje kompilator C #!
Gael Fraiteur
7
@SLaks: przeciwieństwo krótkotrwałego tymczasowego. Trwałe tymczasowe są zasadniczo zmiennymi lokalnymi bez nazw; mają określoną lokalizację na stosie, która żyje przez cały okres użytkowania ramki stosu. Krótkotrwałe elementy tymczasowe są po prostu umieszczane na stosie, gdy potrzebne jest ich przechowywanie, a następnie usuwane, gdy nie są już potrzebne. Trwałe tymczasowe są znacznie łatwiejsze do debugowania, ale mogą znacznie wydłużyć żywotność tymczasowych. Generujemy trwałe tymczasowe, gdy optymalizacje są wyłączone.
Eric Lippert
Mam koncepcję podobną do klas zamknięcia, ale zamiast parametrów podniesionych jako pól, mam je jako zmienne lokalne. Działa to całkiem dobrze w przypadku parametrów, ale jak powiedzieć debugerowi, że „to” nie jest „ldarg.0”, ale zmienną lokalną o indeksie 4? Czy jest jakieś magiczne imię?
Gael Fraiteur
23
@Eric - czy możesz zaktualizować tę odpowiedź przy użyciu nazw wygenerowanych przez C # 5.0 (async / await)? Widziałem nowe przedrostki :)
Gael Fraiteur