visual studio polimorfismo multiple herencia hacer form composicion como classes c# interface multiple-inheritance

studio - Herencia mĂșltiple en C#



herencia y polimorfismo c# (16)

Dado que la herencia múltiple es mala (hace que la fuente sea más complicada) C # no proporciona tal 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 interactuaría entre C #, VB.net y otros idiomas, no porque "haría la fuente más compleja"

MI es un concepto útil, las preguntas sin respuesta son: "¿Qué haces cuando tienes varias 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 podría presentarla un día, pero aún no, CLR ya es compatible con MI pero, como he dicho, no hay construcciones de lenguaje para eso más allá de eso.

Hasta entonces estás atascado con objetos proxy y múltiples interfaces en su lugar :(

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

Por ejemplo, puedo implementar el patrón de herencia múltiple faltante utilizando 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 una manera de inyectar múltiples clases existentes en una nueva clase como es posible en C ++?

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

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

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

Para que no haya necesidad de 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 fácilmente accesible para los desarrolladores de formularios de Windows.

Por lo que sé, actualmente tiene 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 implementa IComponent (aún no lo he intentado).

En ambos casos, necesita trabajar por método y no por clase. Ya que sabe que necesitaremos todos los métodos de TextTcpClient y Component, sería la solución más fácil simplemente combinar esos dos en una clase.

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


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

De todos modos, tengo una pequeña y agradable solución para la 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 firma ... :)


Con C # 8 ahora prácticamente tiene herencia múltiple a través de la implementación predeterminada de los miembros de la interfaz:

interface ILogger { void Log(LogLevel level, string message); void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload } class ConsoleLogger : ILogger { public void Log(LogLevel level, string message) { ... } // Log(Exception) gets default implementation }


Considere usar solo la composition lugar de intentar simular la 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, podría usar la función de 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(); } }


Creé un post-compilador de C # que habilita 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 post-compilador como un evento de Visual Studio post-build-event:

C: / some_path / nroles-v0.1.0-bin / nutate.exe "$ (TargetPath)"

En el mismo montaje lo usas así:

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

En otro montaje lo usas así:

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


Dado que la cuestión de la herencia múltiple (IM) 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 , First , Second , FirstAndSecond , tal como se presentó en la pregunta. IFirst código de muestra a IFirst , ya que el patrón se mantiene igual independientemente del número de interfaces / clases base de MI.

Supongamos que con MI First y Second se derivarían de la misma clase base BaseClass , utilizando solo elementos de interfaz pública de BaseClass

Esto se puede expresar 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, que requieren 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 elementos protegidos / privados de sus clases contenidas, por lo que se puede usar 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"); } }

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


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

En su lugar, fue 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 hechizos" para un juego de rol en el que los efectos debían mezclar en gran medida la función de emparejamiento para obtener una variedad extrema de hechizos sin tener que volver a escribir 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 clase ni siquiera puede usar palabras clave virtuales o abstractas mientras está estática. Las interfaces no pueden usarlas en absoluto.

La codificación parece mucho más rápida y limpia de esta manera. Si solo estás haciendo funciones y no necesitas propiedades heredadas, usa las funciones.


Esto está en la línea de la respuesta de Lawrence Wenham, pero dependiendo de su caso de uso, puede o no ser una mejora, no necesita los instaladores.

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 (). A partir de este punto, básicamente, todo el código de persona va a IGetPerson, no a IPerson, excepto a los nuevos ivars, que tienen que ir a ambos. Y al usar dicho código, no tiene que preocuparse por si su objeto IGetPerson es en realidad un IPerson o no.


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 suficiente cuerda 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 varias clases que tienen un método con la misma firma que el heredero no implementa. ¿Qué método de clase debería elegir? ¿O no debería compilar eso? En general, existe otra forma de implementar la mayoría de las cosas que no se basa en la herencia múltiple.


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 a la perfección, 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; } }


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


Sé que sé, aunque no está permitido y así sucesivamente, en algún momento u realmente lo necesito para esos:

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

como en mi caso, quería hacer esta clase b: Formulario (sí, windows.forms) clase c: b {}

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


Sí, usar la interfaz 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é sucede si ya tenemos una clase con varios métodos pero sin interfaz? 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 secundaria si la clase secundaria va a heredar de la interfaz múltiple.

Al seguir el patrón de diseño de la fachada, podemos simular la herencia de varias clases utilizando los accesores . 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 secundaria ejemplifique las clases primarias.

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 estructura, la clase Child tendrá acceso a todos los métodos y propiedades de Class Father and Mother, simulando la herencia múltiple, heredando una instancia de las clases padre. No es exactamente lo mismo pero es práctico.


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

  1. Y proporcionará la 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 se podría obtener dicha funcionalidad definiendo una clase base que nunca se usa directamente y teniendo una o más clases que heredan directamente de ella sin agregar nada. nuevo (tales clases podrían compartir todo su código, pero no serían sustituibles entre sí). Sin embargo, cualquier lenguaje compatible con CLR permitirá el uso de interfaces que proporcionan la segunda característica de las interfaces (capacidad de sustitución) sin la primera (reutilización de miembros).


Si puede 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 su ejemplo) ... puede hacer lo que pida 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 requeridas deben apoyar la implementación flexible en los métodos de extensión. Cada vez que necesite "agregar métodos a la interfaz" en lugar de eso, agregue un método de extensión.


También me gustaría esto, 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 este blog 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 que esto no se pueda implementar en el compilador de C #, pero es otra complejidad de lenguaje ...