domingo, 2 de marzo de 2008

Herencia múltiple en C# ( resuelta )

Bueno, en realidad no, no hay forma de resolver el tema de la herencia múltiple en C# sin modificar el lenguage, pero siempre podemos acceder a soluciones alternativas mas o menos funcionales para lograr los mismos resultados que queríamos obtener con la herencia múltiple.

Estas soluciones alternativas dependen en general de los medios que poseemos y los resultados esperados. En muchos casos el problema es que tenemos una clase utilitaria que espera objetos de un tipo determinado ( no una interface ), para que nuestro objeto pueda ser utilizado en ese contexto heredamos de aquel y listo:


// Este es el tipo que se usa en la clase utilitaria
public class Usable
{
public virtual void HacerAlgo()
{
Console.WriteLine("Hice algo");
}
}

// Esta es la clase utilitaria
public class Utilitaria
{
public void Usa(Usable u)
{
u.HacerAlgo();
}
}

// Y este es nuestro tipo
class NuestroTipo : Usable
{
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa");
}
}

Hasta aqui todo bien. Pero que pasa si nuestro tipo ya heredaba de otro ? Si hubiera herencia múltiple heredábamos de Usable y de nuestro tipo base pero como no hay siempre podemos instanciar un objeto de tipo Usable y publicarlo para luego usarlo:


class NuestroTipo : NuestroTipoBase
{
private Usable m_usa = new Usable();
public Usable Usar { get { return m_usa; } }
}

...

Utilitaria util = new Utilitaria();
NuestroTipo tipo = new NuestroTipo();
util.Usa(tipo.Usar);

Evidentemente este ejemplo tiene una falla fundamental: esto no tiene nada que ver con la herencia. Si queríamos usar la herencia es porque probablemente hay algo dentro del tipo Usable que queríamos reescribir asi que simplemente instanciarlo dentro del nuestro no resuelve ningún problema. Al final lo que queremos es redefinir el comportamiento del tipo Usable y además queremos en esta redefinición haga uso de las interioridades de nuestro tipo. La solución es crear una clase interna que herede del tipo Usable y que redefina lo que sea necesario, además sería necesario siempre pasarle una instancia de nuestro objeto para poder tener acceso a sus interioridades. Algo asi:


// Este es nuestro tipo que hereda de otro
public class NuestroTipo : NuestroTipoBase
{
// Este es nuestro usable interno
private class NuestroUsable : Usable
{
// Evidentemente necesitamos acceder a las
// interioridades del objeto master
NuestroTipo m_master;
public NuestroUsable(NuestroTipo master)
{
m_master = master;
}
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa ( internamente )");
}
}
private readonly NuestroUsable m_usa;
public Usable Usar { get { return m_usa; } }
public NuestroTipo()
{
m_usa = new NuestroUsable(this);
}
}

Esto resuelve el problema en parte, aunque nos deja todavía con la necesidad de instanciar el objeto, cosa que además tiene que hacerse en el constructor ya que no nos permiten instanciarlo en la declaración pasándole además this como parámetro.

Lo mejor evidentemente sería tener herencia múltiple nativa en el lenguage pero como no hay podemos acudir a este tipo de mecanismos para simularla cuando la necesidad se presenta.