Herencia múltiple en C #

Como la herencia múltiple es mala (hace que la fuente sea más complicada), C # no proporciona ese patrón directamente. Pero a veces sería útil tener esta habilidad.

Por ejemplo, puedo implementar el patrón de herencia múltiple faltante usando interfaces y tres clases como esa:

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 múltiples clases existentes en una nueva clase como es posible en C ++?

Tal vez hay una solución usando algún tipo de generación de código?

O puede verse así (syntax imaginaria 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

Tal vez sería mejor considerar un ejemplo práctico:

Tienes una clase existente (por ejemplo, un cliente TCP basado en texto basado en ITextTcpClient) que ya usas en diferentes ubicaciones dentro de tu proyecto. Ahora sientes la necesidad de crear un componente de tu clase para que sea fácilmente accesible para los desarrolladores de formularios de Windows.

Por lo que 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 usando una instancia de la clase como se muestra con FirstAndSecond.

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

En ambos casos, debe hacer el trabajo por método y no por clase. Como sabes que necesitaremos todos los métodos de TextTcpClient y Component, sería la solución más sencilla combinarlos en una sola clase.

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

    Como la herencia múltiple es mala (hace que la fuente sea más complicada), C # no proporciona ese patrón directamente. Pero a veces sería útil tener esta habilidad.

    C # y .net CLR no han implementado MI porque aún no han concluido cómo funcionaría entre C #, VB.net y los otros lenguajes, no porque “haría la fuente más compleja”

    MI es un concepto útil, las preguntas no respondidas son las siguientes: – “¿Qué haces cuando tienes múltiples clases base comunes en las diferentes superclases?

    Perl es el único idioma con el que he trabajado, donde MI funciona y funciona bien. .Net bien puede presentarlo algún día, pero todavía no, el CLR ya es compatible con MI, pero como ya he dicho, aún no hay construcciones de lenguaje para eso.

    Hasta entonces estás atrapado con objetos proxy y múltiples interfaces en su lugar 🙁

    Considere la posibilidad de usar composición en lugar de intentar simular Herencia Múltiple. Puede usar interfaces para definir qué clases componen la composición, por ejemplo: ISteerable implica una propiedad de tipo SteeringWheel , IBrakable implica 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 los métodos de llamada 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(); } } 

    Me gustaría esto también, es a lo que personalmente me refiero como una mezcla, aunque me doy cuenta de que es un término sobrecargado. Me gustaría poder especificar la variable utilizada para implementar la interfaz, con la opción de proporcionar mi propia implementación para métodos específicos.

    He escrito sobre esto con más detalle , aunque en el contexto de una exageración deliberada de lo que podría significar en términos de herencia.

    No veo ninguna razón por la cual esto no podría implementarse en el comstackdor de C #, pero es otra complejidad de lenguaje …

    Creé un post-comstackdor 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, Does { } 

    Puede ejecutar el comstackdor posterior como un post-build-evento de Visual Studio:

    C: \ some_path \ nroles-v0.1.0-bin \ nutate.exe “$ (TargetPath)”

    En el mismo conjunto, lo utilizas así:

     var fas = new FirstAndSecond(); fas.As().FirstMethod(); fas.As().SecondMethod(); 

    En otro ensamblaje lo usa así:

     var fas = new FirstAndSecond(); fas.FirstMethod(); fas.SecondMethod(); 

    Podría tener una clase base abstracta que implemente tanto IFirst como ISecond, y luego heredar desde esa base.

    MI no está mal, todos los que lo han usado (seriamente) LO AMAN y ¡NO complica el código! Al menos no más que otros constructos pueden complicar el código. El código incorrecto es un código incorrecto independientemente de si MI está o no en la imagen.

    De todos modos, tengo una buena solución para Herencia Múltiple que quería compartir, está en; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog o puede seguir el enlace en mi sig … 🙂

    Si puedes vivir con la restricción de que los métodos de IFirst e ISecond solo deben interactuar con el contrato de IFirst e ISecond (como en tu ejemplo) … puedes hacer lo que solicites con los métodos de extensión. En la práctica, esto rara vez es el caso.

     public interface IFirst {} public interface ISecond {} public class FirstAndSecond : IFirst, ISecond { } public static MultipleInheritenceExtensions { public static void First(this IFirst theFirst) { Console.WriteLine("First"); } public static void Second(this ISecond theSecond) { Console.WriteLine("Second"); } } 

    ///

     public void Test() { FirstAndSecond fas = new FirstAndSecond(); fas.First(); fas.Second(); } 

    Entonces, la idea básica es que defina la implementación requerida en las interfaces … estas cosas obligatorias deberían ser compatibles con la implementación flexible en los métodos de extensión. Cada vez que necesite “agregar métodos a la interfaz” en su lugar, debe agregar un método de extensión.

    Sí, usar Interface es una molestia porque cada vez que agregamos un método en la clase, tenemos que agregar la firma en la interfaz. Además, ¿qué pasa si ya tenemos una clase con muchos métodos pero sin Interface para eso? tenemos que crear manualmente la interfaz para todas las clases de las que queremos heredar. Y lo peor es que tenemos que implementar todos los métodos en las interfaces en la clase hija si la clase hija va a heredar de la interfaz múltiple.

    Al seguir el patrón de diseño de Facade, podemos simular la herencia de múltiples clases utilizando accesos . Declare las clases como propiedades con {get; set;} dentro de la clase que necesita heredar y todas las propiedades y métodos públicos son de esa clase, y en el constructor de la clase hija crea una instancia de las clases principales.

    Por ejemplo:

      namespace OOP { class Program { static void Main(string[] args) { Child somechild = new Child(); somechild.DoHomeWork(); somechild.CheckingAround(); Console.ReadLine(); } } public class Father { public Father() { } public void Work() { Console.WriteLine("working..."); } public void Moonlight() { Console.WriteLine("moonlighting..."); } } public class Mother { public Mother() { } public void Cook() { Console.WriteLine("cooking..."); } public void Clean() { Console.WriteLine("cleaning..."); } } public class Child { public Father MyFather { get; set; } public Mother MyMother { get; set; } public Child() { MyFather = new Father(); MyMother = new Mother(); } public void GoToSchool() { Console.WriteLine("go to school..."); } public void DoHomeWork() { Console.WriteLine("doing homework..."); } public void CheckingAround() { MyFather.Work(); MyMother.Cook(); } } } 

    con esta clase de estructura, Child tendrá acceso a todos los métodos y propiedades de Class Father and Mother, simulando herencia múltiple, heredando una instancia de las clases principales. No es exactamente lo mismo, pero es práctico.

    La herencia múltiple es una de esas cosas que generalmente causa más problemas de los que resuelve. En C ++ se ajusta al patrón de darle cuerda suficiente para colgarse, pero Java y C # han elegido ir por la ruta más segura de no darle la opción. El mayor problema es qué hacer si hereda múltiples clases que tienen un método con la misma firma que el heredado no implementa. ¿Qué método de clase debería elegir? ¿O no debería comstackr? En general, hay otra forma de implementar la mayoría de las cosas que no depende de la herencia múltiple.

    Si X hereda de Y, tiene dos efectos algo ortogonales:

    1. Y proporcionará funcionalidad predeterminada para X, por lo que el código para X solo tiene que incluir cosas que sean diferentes de Y.
    2. Casi en cualquier lugar se esperaría una Y, en su lugar se puede usar una X.

    Aunque la herencia proporciona ambas características, no es difícil imaginar circunstancias en las que cualquiera de las dos podría ser útil sin la otra. Ningún lenguaje .net que conozco tiene una forma directa de implementar el primero sin el segundo, aunque uno podría obtener dicha funcionalidad definiendo una clase base que nunca se usa directamente, y teniendo una o más clases que heredan directamente sin agregar nada nuevo (dichas clases podrían compartir todo su código, pero no serían sustituibles entre sí). Sin embargo, cualquier lenguaje que cumpla con CLR permitirá el uso de interfaces que proporcionan la segunda característica de las interfaces (sustituibilidad) sin la primera (reutilización de miembros).

    Sé que sé que aunque no está permitido, y así sucesivamente, en algún momento lo necesitas para los que:

     class a {} class b : a {} class c : b {} 

    como en mi caso, quería hacer esta clase b: forma (sí, las ventanas.formas) clase c: b {}

    porque la mitad de la función era idéntica y con la interfaz debes reescribirlos todos

    Dado que la cuestión de la herencia múltiple (MI) aparece de vez en cuando, me gustaría agregar un enfoque que aborde algunos problemas con el patrón de composición.

    Me IFirst en el IFirst ISecond IFirst , ISecond , First , Second , FirstAndSecond Second , FirstAndSecond como se presentó en la pregunta. IFirst código de muestra a IFirst , ya que el patrón permanece igual independientemente del número de interfaces / clases base MI.

    Supongamos, que con MI First y Second ambos derivan de la misma clase base BaseClass , usando solo elementos de interfaz pública de BaseClass

    Esto se puede express agregando una referencia de contenedor a BaseClass en la First y Second implementación:

     class First : IFirst { private BaseClass ContainerInstance; First(BaseClass container) { ContainerInstance = container; } public void FirstMethod() { Console.WriteLine("First"); ContainerInstance.DoStuff(); } } ... 

    Las cosas se vuelven más complicadas, cuando se hace referencia a elementos de interfaz protegidos de BaseClass o cuando First y Second serían clases abstractas en MI, requiriendo que sus subclases implementen algunas partes abstractas.

     class BaseClass { protected void DoStuff(); } abstract class First : IFirst { public void FirstMethod() { DoStuff(); DoSubClassStuff(); } protected abstract void DoStuff(); // base class reference in MI protected abstract void DoSubClassStuff(); // sub class responsibility } 

    C # permite que las clases anidadas accedan a los elementos protegidos / privados de las clases que los contienen, por lo que puede usarse para vincular los bits abstractos de la First implementación.

     class FirstAndSecond : BaseClass, IFirst, ISecond { // link interface private class PartFirst : First { private FirstAndSecond ContainerInstance; public PartFirst(FirstAndSecond container) { ContainerInstance = container; } // forwarded references to emulate access as it would be with MI protected override void DoStuff() { ContainerInstance.DoStuff(); } protected override void DoSubClassStuff() { ContainerInstance.DoSubClassStuff(); } } private IFirst partFirstInstance; // composition object public FirstMethod() { partFirstInstance.FirstMethod(); } // forwarded implementation public FirstAndSecond() { partFirstInstance = new PartFirst(this); // composition in constructor } // same stuff for Second //... // implementation of DoSubClassStuff private void DoSubClassStuff() { Console.WriteLine("Private method accessed"); } } 

    Hay bastante repetitivo, pero si la implementación real de FirstMethod y SecondMethod es lo suficientemente compleja y la cantidad de métodos privados / protegidos a los que se accede es moderada, entonces este patrón puede ayudar a superar la herencia múltiple que falta.

    Esto es similar a la respuesta de Lawrence Wenham, pero dependiendo de su caso de uso, puede o no ser una mejora, no necesita a los incubadores.

     public interface IPerson { int GetAge(); string GetName(); } public interface IGetPerson { IPerson GetPerson(); } public static class IGetPersonAdditions { public static int GetAgeViaPerson(this IGetPerson getPerson) { // I prefer to have the "ViaPerson" in the name in case the object has another Age property. IPerson person = getPerson.GetPersion(); return person.GetAge(); } public static string GetNameViaPerson(this IGetPerson getPerson) { return getPerson.GetPerson().GetName(); } } public class Person: IPerson, IGetPerson { private int Age {get;set;} private string Name {get;set;} public IPerson GetPerson() { return this; } public int GetAge() { return Age; } public string GetName() { return Name; } } 

    Ahora cualquier objeto que sepa cómo obtener una persona puede implementar IGetPerson, y automáticamente tendrá los métodos GetAgeViaPerson () y GetNameViaPerson (). Desde este punto, básicamente todo el código de persona va a IGetPerson, no a IPerson, a excepción de los nuevos ivars, que tienen que ir a ambos. Y al usar dicho código, no tiene que preocuparse de si su objeto IGetPerson es realmente un IPerson o no.

    Se puede lograr una herencia múltiple en C # utilizando interfaces. Se puede explicar con la ayuda de un ejemplo:

     using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MultipleInheritApplication { interface calc1 { int add(int a, int b); } interface calc2 { int sub(int x, int y); } interface calc3 { int mul(int r, int s); } interface calc4 { int div(int c, int d); } class Calculation : calc1, calc2, calc3, calc4 { public int result1; public int add(int a, int b) { return result1 = a + b; } public int result2; public int sub(int x, int y) { return result2 = x - y; } public int result3; public int mul(int r, int s) { return result3 = r * s; } public int result4; public int div(int c, int d) { return result4 = c / d; } class Program { static void Main(string[] args) { Calculation c = new Calculation(); c.add(8, 2); c.sub(20, 10); c.mul(5, 2); c.div(20, 10); Console.WriteLine("Multiple Inheritance concept Using Interfaces :\n "); Console.WriteLine("Addition: " + c.result1); Console.WriteLine("Substraction: " + c.result2); Console.WriteLine("Multiplication :" + c.result3); Console.WriteLine("Division: " + c.result4); Console.ReadKey(); } } } } 

    Parece que todos nos dirigimos hacia la ruta de la interfaz con esto, pero la otra posibilidad obvia, aquí, es hacer lo que se supone que debe hacer OOP, y construir su árbol de herencia … (¿no es esto lo que el diseño de clase es todo ¿acerca de?)

     class Program { static void Main(string[] args) { human me = new human(); me.legs = 2; me.lfType = "Human"; me.name = "Paul"; Console.WriteLine(me.name); } } public abstract class lifeform { public string lfType { get; set; } } public abstract class mammal : lifeform { public int legs { get; set; } } public class human : mammal { public string name { get; set; } } 

    Esta estructura proporciona bloques de código reutilizables y, seguramente, ¿cómo debe escribirse el código OOP?

    Si este enfoque particular no encaja en la factura, simplemente creamos nuevas clases basadas en los objetos requeridos …

     class Program { static void Main(string[] args) { fish shark = new fish(); shark.size = "large"; shark.lfType = "Fish"; shark.name = "Jaws"; Console.WriteLine(shark.name); human me = new human(); me.legs = 2; me.lfType = "Human"; me.name = "Paul"; Console.WriteLine(me.name); } } public abstract class lifeform { public string lfType { get; set; } } public abstract class mammal : lifeform { public int legs { get; set; } } public class human : mammal { public string name { get; set; } } public class aquatic : lifeform { public string size { get; set; } } public class fish : aquatic { public string name { get; set; } } 

    En mi propia implementación, descubrí que el uso de clases / interfaces para MI, aunque “buena forma”, tendía a ser una gran complicación ya que necesita configurar toda esa herencia múltiple solo para unas pocas llamadas a funciones necesarias, y en mi caso, necesitaba hacerse literalmente docenas de veces de manera redundante.

    En cambio, era más fácil simplemente hacer “funciones estáticas que llaman funciones que llaman funciones” en diferentes variedades modulares como una especie de reemplazo OOP. La solución en la que estaba trabajando era el “sistema de hechizo” para un juego de rol donde los efectos necesitan combinar funciones y combinaciones para ofrecer una variedad extrema de hechizos sin volver a escribir el código, como parece indicar el ejemplo.

    La mayoría de las funciones ahora pueden ser estáticas porque no necesariamente necesito una instancia para la lógica de hechizos, mientras que la herencia de clases ni siquiera puede usar palabras clave virtuales o abstractas mientras estática. Las interfaces no pueden usarlas en absoluto.

    La encoding parece mucho más rápida y más limpia de esta manera IMO. Si solo está haciendo funciones y no necesita propiedades heredadas, use funciones.