Gerardo Contijoch

Experiencias del día a día trabajando con .NET – ASP.NET, C#, ASP.NET MVC y demas…

Simplificar la depuración con el uso de DebuggerTypeProxyAttribute

Posted by Gerardo Contijoch en marzo 25, 2009

En el último post les hable sobre DebuggerDisplayAttribute, un atributo que nos permite cambiar la información que nos muestra el debugger de Visual Studio sobre un tipo. Ahora les voy a presentar otro atributo muy similar, pero cuya función es cambiar completamente lo que se muestra de un tipo, incluso sus propiedades. Estoy hablando de DebuggerTypeProxyAttribute. Al aplicar este atributo sobre una clase o estructura lo que hacemos es decirle al debugger que no use nuestra clase (o estructura) cuando inspeccionemos un objeto de ese tipo en el Debugger, sino que use un proxy a la misma. La idea detrás de esto es ocultar los miembros de nuestra clase (porque no son relevantes posiblemente) y mostrar los del proxy, los cuales son los que nos van a interesar ver cuando estemos depurando nuestro código.

Veamos como se vería un posible proxy para una clase Persona (tomada del post anterior y modificada ligeramente) junto a ella:

   1: [DebuggerDisplay("Nombre: {Nombre, nq}, Año de nacimiento: {FechaDeNacimiento.Year}, ¿Es Rosarino?: {Ciudad == \"Rosario\" ? \"Sí\" : \"No\", nq}")]
   2: public class Persona {
   3:     public string Nombre { get; set; }
   4:     public string Apellido { get; set; }
   5:     public int Edad { get; set; }
   6:     public string Ciudad { get; set; }
   7:     public DateTime FechaDeNacimiento { get; set; }
   8:
   9:     public bool EstaCasado { get; set; }
  10:     public Persona Conyuge { get; set; }
  11:
  12:     public Persona Padre { get; set; }
  13:     public Persona Madre { get; set; }
  14:
  15:     public IEnumerable<Persona> Hermanos { get; }
  16:
  17:     public bool PracticaDeportes { get; set; }
  18:     public IEnumerable<string> Deportes { get; }
  19:
  20:     public IEnumerable<string> Estudios { get; }
  21:
  22:     /// <summary>
  23:     /// Proxy para la clase Persona.
  24:     /// </summary>
  25:     internal class PersonaDebugView {
  26:         private Persona Persona { get; set; }
  27:
  28:         public PersonaDebugView(Persona p) {
  29:             Persona = p;
  30:         }
  31:
  32:         public string Nombre {
  33:             get {
  34:                 if (Persona.Madre != null) {
  35:                     return string.Format("{0} {1} {2}", Persona.Nombre, Persona.Apellido, Persona.Madre.Apellido);
  36:                 }
  37:
  38:                 return string.Format("{0} {1}", Persona.Nombre, Persona.Apellido);
  39:             }
  40:         }
  41:
  42:         public string NombreConyuge {
  43:             get {
  44:                 if (Persona.EstaCasado) {
  45:                     return Persona.Conyuge.Nombre;
  46:                 }
  47:
  48:                 return "No tiene conyuge";
  49:             }
  50:         }
  51:
  52:         public bool EsMayorDeEdad {
  53:             get { return Persona.Edad >= 21; }
  54:         }
  55:
  56:         public string PaisDeOrigen {
  57:             get { return RecuperarPaisAPartirDeCiudad(Persona.Ciudad); }
  58:         }
  59:     }
  60: }

Quisiera aclarar un par de puntos en este ejemplo. Primero, vemos que la clase proxy se encuentra dentro de la clase Persona. Esto es recomendable ya de ese modo el proxy puede tener acceso a los miembros privados de la clase que lo contiene y tiene el beneficio extra de que no se mezcla con el resto de las clases de nuestra aplicación. También es un requisito que el constructor del proxy tenga un parámetro del tipo de la clase que se desea ocultar.

Otro detalle que puede haber saltado para los que no leyeron el post anterior (¿qué están esperando?), es el uso de DebuggerDisplayAttribute. Si les interesa saber porque lo uso, pueden encontrar acá el motivo.

Si depuramos nuestra aplicación (aún sin haber aplicado el atributo DebuggerTypeProxyAttribute), veremos que la clase Persona se ve así en el debugger:

DebuggerProxyType-persona

Son muchos datos para una persona y si la mayoría de ellos no nos interesa (recordemos que estamos trabajando sobre un ejemplo) puede volverse un poco incómodo inspeccionar nuestras variables. Veamos ahora como se vería esta pantalla si aplicamos el atributo DebuggerTypeProxyAttribute de la siguiente manera:

   1: [DebuggerTypeProxy(typeof(PersonaDebugView))]
   2: [DebuggerDisplay(/*...*/)]
   3: public class Persona {
   4:     /*...*/
   5: }

DebuggerProxyType-personaconProxy

Las propiedades que estamos viendo son las del proxy (que no tienen que corresponderse necesariamente con las de la clase Persona) y nos muestran una vista totalmente diferente de la variable que estamos inspeccionando.

Presten atención a un nuevo miembro: RawView. Éste último, generado automáticamente, lo que hace es mostrarnos la vista original o sin modificar de nuestra clase y si lo abrimos veremos que tenemos acceso a todas las propiedades de la clase Persona, tal cual la definimos.

El uso de este atributo es muy común en las colecciones en donde se ocultan muchos miembros y solo se muestran unos pocos que son los más relevantes a la hora de trabajar con una colección. Siempre que inspeccionen una variable y vean el miembro RawView, van a estar viendo un proxy para esa clase y no su verdadera implementación.

Aplicando el atributo a nivel de ensamblado

Al igual que con DebuggerDisplayAttribute, DebuggerTypeProxyAttribute puede usarse a nivel de ensamblado exactamente de la misma manera. En el post anterior explico en detalle como se hace esto.

Nos vemos en el próximo post!

Publicado originalmente en https://gerardocontijoch.wordpress.com.

2 comentarios to “Simplificar la depuración con el uso de DebuggerTypeProxyAttribute”

  1. vhspiceros said

    excelente aporte.

    esta muy bueno tu blog, ya lo agrege a mi google reader. asi que me vaz a tener mas seguido por aca.

    Felicitaciones por tu blog. muy bueno :).

  2. Gerardo Contijoch said

    Me alegro que te resulte interesante el blog. Hace un tiempo que sigo tu blog y si lo hago es porque definitivamente encuentro interesantes también a tus aportes.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

 
A %d blogueros les gusta esto: