En el post anterior, vimos como habilitar la modificación de la estructura de una clase generada (o interfaz, o estructura) utilizando el keyword partial. De esta manera, podemos agregar nuevos métodos, campos, propiedades y eventos a una clase definida en otros archivos. Siguiendo con el tema, voy a presentar otro uso, que nos permite definir métodos opcionales.
Los partial methods funcionan en forma similar a los events, o a los delegates, pero sin tener costo en tiempo de ejecución en caso de no recibir una implementación, ya que el compilador elimina todas las referencias existentes al método en caso de no recibir una implementación.
Un partial method consta de dos partes: la definición, donde se indica el nombre y los parámetros que recibe el método (siempre retorna void); y la implementación, donde se repiten los mismos datos, pero se incluye un cuerpo que implementa el método.
Veamos un ejemplo. Supongamos que tenemos un generador que produce el siguiente código:
partial class Person
{
string name;
string surname;
public string Name
{
get { return name; }
set { name = value; }
}
public string Surname
{
get { return surname; }
set { surname = value; }
}
}
Con esta clase así generada, es difícil permitir a nuestros usuarios saber cuando cambió el nombre o el apellido de nuestra persona. Una posibilidad que tenemos es la de cambiar el generador para que agregue un evento por cada propiedad:
partial class Person
{
string name;
string surname;
public event Action<string> NameChanged = delegate {};
public event Action<string> SurnameChanged = delegate {};
public string Name
{
get { return name; }
set { name = value; NameChanged(name);}
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Esta versión es correcta, pero en el caso en que no requiramos monitorear todas las propiedades, hemos agregado eventos en forma innecesaria. Además, es una solución demasiado flexible, ya que puede haber múltiples suscriptores al evento, lo cual puede ser un problema.
La alternativa con partial methods sería esta:
partial class Person
{
string name;
string surname;
partial void NameChanged(string name);
partial void SurnameChanged(string surname);
public string Name
{
get { return name; }
set { name = value; NameChanged(name); }
}
public string Surname
{
get { return surname; }
set { surname = value; SurnameChanged(name); }
}
}
Si queremos saber cuando cambió la propiedad Name, podemos definir en otro archivo:
partial class Person
{
partial void NameChanged(string name)
{
Console.WriteLine("Name changed: '{0}'", name);
}
}
En este caso, la implementación de Person, tiene dos propiedades, Name y Surname, y únicamente un método, NameChanged. Cómo no definimos una implementación para SurnameChanged, el compilador omite todas las llamadas al método y lo elimina de la clase.
La generación de código es algo muy útil, que nos permite automatizar varias tareas mundanas, lo que mejora nuestra productividad. El uso de partial classes y partial methods nos permite mejorar las posibilidades de reúso de nuestros generadores.