Dado que la herencia múltiple es mala (hace que la fuente sea más complicada) C # no proporciona dicho 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í (sintaxis imaginaria de C #):
public class FirstAndSecond: IFirst from First, ISecond from Second
{ }
Para que no sea 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 los desarrolladores de formularios de Windows puedan acceder fácilmente.
Hasta donde sé, actualmente tienes dos formas de hacer esto:
Escriba una nueva clase que se herede de los componentes e implemente la interfaz de la clase TextTcpClient usando una instancia de la clase en sí como se muestra con FirstAndSecond.
Escriba una nueva clase que herede de TextTcpClient y de alguna manera implemente IComponent (aún no lo he probado).
En ambos casos, debe trabajar por método y no por clase. Como sabe que necesitaremos todos los métodos de TextTcpClient y Component, sería la solución más fácil 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 después, pero escribir esto a mano es un puro dolor de cabeza.
fuente
Respuestas:
C # y .net CLR no han implementado MI porque aún no han concluido cómo interactuarí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 sin respuesta son como: - "¿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. Es posible que .Net lo presente algún día, pero aún no, el CLR ya admite MI pero, como he dicho, todavía no hay construcciones de lenguaje para eso.
Hasta entonces, estás atrapado con objetos proxy y múltiples interfaces en su lugar :(
fuente
Considere simplemente usar la composición en lugar de tratar de simular herencia múltiple. Puede usar Interfaces para definir qué clases componen la composición, por ejemplo:
ISteerable
implica una propiedad de tipoSteeringWheel
,IBrakable
implica una propiedad de tipoBrakePedal
, 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:
fuente
myCar
ha terminado de girar la dirección antes de llamarStop
. Podría volcarse si seStop
aplica mientrasmyCar
está a una velocidad excesiva. : DCreé un postcompilador C # que permite este tipo de cosas:
Puede ejecutar el compilador posterior como un evento posterior a la compilación de Visual Studio:
En el mismo ensamblaje lo usas así:
En otro ensamblaje lo usas así:
fuente
Podría tener una clase base abstracta que implemente tanto IFirst como ISecond, y luego heredar solo de esa base.
fuente
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 herencia múltiple que quería compartir, es; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog o puede seguir el enlace en mi sig ... :)
fuente
myFoo
es de tipoFoo
, que hereda deMoo
yGoo
, ambos heredan deBoo
, entonces(Boo)(Moo)myFoo
y(Boo)(Goo)myFoo
no serían equivalentes). ¿Conoces algún enfoque para preservar la identidad?En mi propia implementación, descubrí que el uso de clases / interfaces para MI, aunque "buena forma", tendía a ser una complicación enorme, ya que necesita configurar toda esa herencia múltiple para solo unas pocas llamadas de función necesarias, y en mi caso, necesitaba hacerse literalmente docenas de veces de forma redundante.
En cambio, era más fácil simplemente hacer "funciones que llaman funciones que llaman funciones" estáticas en diferentes variedades modulares como una especie de reemplazo de 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 y combinar llamadas de funciones para dar una variedad extrema de hechizos sin reescribir el código, como parece indicar el ejemplo.
La mayoría de las funciones ahora pueden ser estáticas porque no necesito necesariamente una instancia para la lógica ortográfica, mientras que la herencia de clase ni siquiera puede usar palabras clave virtuales o abstractas mientras está estática. Las interfaces no pueden usarlos en absoluto.
La codificación parece mucho más rápida y limpia de esta manera en mi opinión. Si solo está haciendo funciones y no necesita propiedades heredadas , use funciones.
fuente
Con C # 8 ahora prácticamente tiene herencia múltiple a través de la implementación predeterminada de los miembros de la interfaz:
fuente
new ConsoleLogger().Log(someEception)
: simplemente no funcionará, tendría que convertir explícitamente su objeto aILogger
para usar el método de interfaz predeterminado. Por lo tanto, su utilidad es algo limitada.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 le pida con los métodos de extensión. En la práctica, este rara vez es el caso.
///
Entonces, la idea básica es que defina la implementación requerida en las interfaces ... este material requerido debe admitir la implementación flexible en los métodos de extensión. Cada vez que necesite "agregar métodos a la interfaz", agregue un método de extensión.
fuente
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é pasa si ya tenemos una clase con un montón de métodos pero no tenemos una 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 para que la clase secundaria herede de la interfaz múltiple.
Siguiendo el patrón de diseño de Fachada, podemos simular la herencia de múltiples clases usando 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 instancia las clases primarias.
Por ejemplo:
con esta estructura, la clase Child tendrá acceso a todos los métodos y propiedades de la clase Father and Mother, simulando herencia múltiple, heredando una instancia de las clases padre. No es lo mismo pero es práctico.
fuente
Quick Actions and Refactorings...
esto es algo que debe saber, le ahorrará mucho tiempoTodos parecemos estar avanzando por 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?)
Esta estructura proporciona bloques de código reutilizables y, seguramente, ¿cómo se debe escribir el código OOP?
Si este enfoque en particular no se ajusta a la factura, simplemente creamos nuevas clases basadas en los objetos requeridos ...
fuente
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 optado 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 heredado no implementa. ¿Qué método de clase debería elegir? ¿O no debería eso compilar? Generalmente hay otra forma de implementar la mayoría de las cosas que no depende de la herencia múltiple.
fuente
Si X hereda de Y, eso tiene dos efectos algo ortogonales:
Aunque la herencia proporciona ambas características, no es difícil imaginar circunstancias en las que cualquiera 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 hereden 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 (sustituibilidad) sin la primera (reutilización de miembros).
fuente
Sé que lo sé a pesar de que no está permitido, etc., en algún momento realmente lo necesitas para aquellos:
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 debes reescribirlas todas
fuente
class a : b, c
(implementando los agujeros contractuales necesarios). ¿Quizás tus ejemplos están un poco simplificados?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.
Construyo sobre el
IFirst
,ISecond
,First
,Second
,FirstAndSecond
enfoque, tal como se presentó en la pregunta. Reduzco el código de muestra aIFirst
, ya que el patrón permanece igual independientemente del número de interfaces / clases base de MI.Supongamos que con MI
First
ySecond
ambos derivarían de la misma clase baseBaseClass
, usando solo elementos de interfaz pública deBaseClass
Esto se puede expresar agregando una referencia de contenedor a
BaseClass
la implementaciónFirst
ySecond
:Las cosas se vuelven más complicadas, cuando
BaseClass
se hace referencia a elementos de interfaz protegidos o cuandoFirst
ySecond
serían clases abstractas en MI, lo que requiere que sus subclases implementen algunas partes abstractas.C # permite que las clases anidadas accedan a elementos protegidos / privados de sus clases que contienen, por lo que esto puede usarse para vincular los bits abstractos de la
First
implementación.Hay bastante repetitivo, 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, entonces este patrón puede ayudar a superar la falta de herencia múltiple.
fuente
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 preparadores.
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, excepto los nuevos ivars, que deben ir a ambos. Y al usar dicho código, no tiene que preocuparse por si su objeto IGetPerson es en realidad un IPerson.
fuente
Esto ahora es posible a través de
partial
clases, cada una de ellas puede heredar una clase por sí misma, haciendo que el objeto final herede todas las clases base. Puede obtener más información al respecto aquí .fuente