Herencia múltiple en C#

Resuelto Martin asked hace 15 años • 13 respuestas

Dado que la herencia múltiple es mala (hace que el código fuente sea más complicado), C# no proporciona dicho patrón directamente. Pero a veces sería útil tener esta capacidad.

Por ejemplo, puedo implementar el patrón de herencia múltiple que falta usando interfaces y tres clases así:

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class First:IFirst 
{ 
    public void FirstMethod() { Console.WriteLine("First"); } 
}

public class Second:ISecond 
{ 
    public void SecondMethod() { Console.WriteLine("Second"); } 
}

public class FirstAndSecond: IFirst, ISecond
{
    First first = new First();
    Second second = new Second();
    public void FirstMethod() { first.FirstMethod(); }
    public void SecondMethod() { second.SecondMethod(); }
}

Cada vez que agrego un método a una de las interfaces , también necesito cambiar la clase FirstAndSecond .

¿Hay alguna manera de inyectar varias clases existentes en una nueva clase como es posible en C++?

¿Quizás haya una solución mediante algún tipo de generación de código?

O puede verse así (sintaxis imaginaria de C#):

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }

De modo que no será necesario actualizar la clase FirstAndSecond cuando modifique una de las interfaces.


EDITAR

Quizás sería mejor considerar un ejemplo práctico:

Tiene una clase existente (por ejemplo, un cliente TCP basado en texto basado en ITextTcpClient) que ya utiliza en diferentes ubicaciones dentro de su proyecto. Ahora siente la necesidad de crear un componente de su clase para que sea de fácil acceso para los desarrolladores de formularios de Windows.

Hasta donde yo sé, actualmente tienes dos formas de hacer esto:

  1. Escriba una nueva clase que se herede de los componentes e implemente la interfaz de la clase TextTcpClient utilizando una instancia de la clase misma como se muestra con FirstAndSecond.

  2. Escriba una nueva clase que herede de TextTcpClient y de alguna manera implemente IComponent (aún no lo he probado).

En ambos casos es necesario trabajar por método y no por clase. Como sabe que necesitaremos todos los métodos de TextTcpClient y Component, la solución más sencilla sería combinar esos dos en una sola clase.

Para evitar conflictos, esto se puede hacer mediante la generación de código, donde el resultado podría modificarse posteriormente, pero escribir esto a mano es un verdadero dolor de cabeza.

Martin avatar Oct 07 '08 20:10 Martin
Aceptado

Considere simplemente usar composición en lugar de intentar simular herencia múltiple. Puede utilizar Interfaces para definir qué clases componen la composición, por ejemplo: ISteerableimplica una propiedad de tipo SteeringWheel, IBrakableimplica una propiedad de tipo BrakePedal, etc.

Una vez que haya hecho eso, puede usar la función Métodos de extensión agregada a C# 3.0 para simplificar aún más la llamada a métodos en esas propiedades implícitas, por ejemplo:

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}
Chris Wenham avatar Oct 07 '2008 13:10 Chris Wenham

Dado que la herencia múltiple es mala (hace que el código fuente sea más complicado), C# no proporciona dicho patrón directamente. Pero a veces sería útil tener esta capacidad.

C# y .net CLR no han implementado MI porque aún no han llegado a una conclusión sobre cómo interoperaría entre C#, VB.net y los otros lenguajes, no porque "haría que el código fuente fuera más complejo".

MI es un concepto útil, las preguntas sin respuesta son como: "¿Qué haces cuando tienes múltiples clases base comunes en las diferentes superclases?

Perl es el único lenguaje con el que he trabajado donde MI funciona y funciona bien. Es posible que .Net lo presente algún día, pero todavía no, CLR ya admite MI pero, como dije, todavía no existen construcciones de lenguaje más allá de eso.

Hasta entonces, estarás atrapado con objetos Proxy y múltiples Interfaces :(

IanNorton avatar Nov 08 '2009 07:11 IanNorton

Creé un postcompilador de C# que permite este tipo de cosas:

using NRoles;

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class RFirst : IFirst, Role {
  public void FirstMethod() { Console.WriteLine("First"); }
}

public class RSecond : ISecond, Role {
  public void SecondMethod() { Console.WriteLine("Second"); }
}

public class FirstAndSecond : Does<RFirst>, Does<RSecond> { }

Puede ejecutar el postcompilador como un evento posterior a la compilación de Visual Studio:

C:\alguna_ruta\nroles-v0.1.0-bin\nutate.exe "$(TargetPath)"

En el mismo ensamblado lo usas así:

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();

En otro ensamblado lo usas así:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();
Jordão avatar May 14 '2011 14:05 Jordão