Dónde aprender sobre los 'nombres mágicos' del depurador VS

110

Si alguna vez ha usado Reflector, probablemente haya notado que el compilador de C # genera tipos, métodos, campos y variables locales que merecen una visualización 'especial' por parte del depurador. Por ejemplo, las variables locales que comienzan con 'CS $' no se muestran al usuario. Hay otras convenciones de nomenclatura especiales para tipos de cierre de métodos anónimos, campos de respaldo de propiedades automáticas, etc.

Mi pregunta: ¿dónde aprender sobre estas convenciones de nomenclatura? ¿Alguien sabe algo de documentación?

Mi objetivo es hacer que PostSharp 2.0 use las mismas convenciones.

Gael Fraiteur
fuente

Respuestas:

209

Estos son detalles de implementación no documentados del compilador y están sujetos a cambios en cualquier momento. (ACTUALIZACIÓN: consulte GeneratedNames.cs las fuentes de C # para obtener los detalles actuales; la descripción a continuación está algo desactualizada).

Sin embargo, dado que soy un buen tipo, estos son algunos de esos detalles:

Si tiene una variable local sin usar que el optimizador elimina, de todos modos emitimos información de depuración en el PDB. Colocamos el sufijo __Deleted$en tales variables para que el depurador sepa que estaban en el código fuente pero no representadas en el binario.

Los espacios de variables temporales asignados por el compilador reciben nombres con el patrón CS $ X $ Y, donde X es el "tipo temporal" e Y es el número de temporales asignados hasta ahora. Los tipos temporales son:

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.  

Los tipos temporales entre 8 y 264 son almacenamientos de índice de matriz adicionales para matrices multidimensionales.

Los tipos temporales por encima de 264 se utilizan para temporales que involucran la instrucción fija que fija una cuerda.

Se generan nombres especiales generados por el compilador para:

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")

El patrón para generar nombres mágicos es: P<N>C__SIdonde:

  • P es CS $ para delegados almacenados en caché e instancias de clase de visualización; de lo contrario, está vacío.
  • N es el nombre original asociado con la cosa, si existe
  • C es el carácter del 1 al s enumerados anteriormente
  • S es un sufijo descriptivo ("actual", "estado", etc.) para que no tenga que memorizar la tabla anterior al leer los metadatos.
  • I es un número único opcional
Eric Lippert
fuente
2
¡Gracias! ¡Veré si puedo hacer que las clases de cierre de PostSharp se comporten tan bien como lo que genera el compilador de C #!
Gael Fraiteur
7
@SLaks: Lo opuesto a un temporal de corta duración. Los temporales duraderos son esencialmente variables locales sin nombres; tienen una ubicación específica en la pila que permanece durante toda la vida útil del marco de la pila. Los temporales de corta duración simplemente se colocan en la pila cuando se necesita su almacenamiento y luego se quitan cuando ya no se necesitan. Los temporales duraderos son mucho más fáciles de depurar, pero pueden alargar la vida útil de los temporales. Generamos provisionales duraderos cuando las optimizaciones están desactivadas.
Eric Lippert
Tengo un concepto similar a las clases de cierre, pero en lugar de tener parámetros como campos, los tengo como variables locales. Esto funciona bastante bien para los parámetros, pero ¿cómo decirle al depurador que 'esto' no es 'ldarg.0' sino la variable local con índice 4? ¿Hay algún nombre mágico?
Gael Fraiteur
23
@Eric: ¿podrías actualizar esta respuesta con los nombres generados por C # 5.0 (async / await)? He visto algunos prefijos nuevos :)
Gael Fraiteur