over multiple method inherited inherit example c# oop inheritance design-patterns

c# - multiple - composition over inheritance



Una forma de convertir un tipo base en un tipo derivado (17)

¡Prueba la composición en lugar de la herencia!

Me parece que sería mejor pasar una instancia de SomeBaseClass a SomeDerivedClass (que ya no derivará la clase base, y debería renombrarse como tal)

public class BooleanHolder{ public bool BooleanEvaluator {get;set;} } public class DatabaseInserter{ BooleanHolder holder; public DatabaseInserter(BooleanHolder holder){ this.holder = holder; } public void Insert(SqlConnection connection) { ...random connection stuff cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar; ... } } public static void Main(object[] args) { BooleanHolder h = new BooleanHolder(); DatabaseInserter derClass = new DatabaseInserter(h); derClass.Insert(new sqlConnection); }

Consulte http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (página 3):

Reutilización del código a través de la composición La composición proporciona una forma alternativa para Apple de reutilizar la implementación de Peel () de Fruit. En lugar de extender Fruit, Apple puede contener una referencia a una instancia de Fruit y definir su propio método peel () que simplemente invoca a peel () en Fruit.

No estoy seguro si esto es algo extraño de hacer o no, o si es de alguna manera el olor del código ... pero me preguntaba si había una forma (algún tipo de patrón de oop sería bueno) de "lanzar" un tipo base a una forma de su tipo derivado. Sé que esto tiene poco sentido ya que el tipo derivado tendrá una funcionalidad adicional que el padre no ofrece, que en sí mismo no es fundamentalmente sólido. Pero, ¿hay alguna forma de hacer esto? Aquí hay un ejemplo de código para poder explicar mejor lo que estoy preguntando.

public class SomeBaseClass { public string GetBaseClassName {get;set;} public bool BooleanEvaluator {get;set;} } public class SomeDerivedClass : SomeBaseClass { public void Insert(SqlConnection connection) { //...random connection stuff cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar; //... } } public static void Main(object[] args) { SomeBaseClass baseClass = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)baseClass; derClass.Insert(new sqlConnection()); }

Sé que esto parece tonto, pero ¿hay alguna manera de lograr algo de este tipo?


¿Has pensado en una interfaz que implementarían tanto tu clase base como tu clase derivada? No sé los detalles de por qué está implementando de esta manera, pero podría funcionar.


C ++ lo maneja usando un constructor. Typecasting de C ++ . Parece un descuido para mí. Muchos de ustedes han sacado a relucir la cuestión de qué haría el proceso con las propiedades adicionales. Yo respondería, ¿qué hace el compilador cuando crea la clase derivada cuando el programador no establece las propiedades? He manejado esta situación similar a C ++. Creo un constructor que toma la clase base y luego establece manualmente las propiedades en el constructor. Esto es definitivamente preferible a establecer una variable en la clase derivada y romper la herencia. También lo elegiría sobre un método de fábrica porque creo que el código resultante sería más limpio.


Como muchas respuestas han señalado, no se puede abatir, lo que tiene sentido.

Sin embargo, en su caso, SomeDerivedClass no tiene propiedades que "perderán". Entonces podrías crear un método de extensión como este:

public static T ToDerived<T>(this SomeBaseClass baseClass) where T:SomeBaseClass, new() { return new T() { BooleanEvaluator = baseClass.BooleanEvaluator, GetBaseClassName = baseClass.GetBaseClassName }; }

Entonces no estás lanzando, solo convirtiendo:

SomeBaseClass b = new SomeBaseClass(); SomeDerivedClass c = b.ToDerived<SomeDerivedClass>();

Esto solo funciona realmente si todos los datos en la clase base están en forma de propiedades legibles y modificables.


Como otros han notado, el casting que sugieres no es realmente posible. ¿Sería tal vez un caso en el que se pueda introducir el patrón Decorador (extracto de Head First)?


Downcasting tiene sentido, si tiene un Object de clase derivada pero está referenciado por una referencia de tipo de clase base y por alguna razón desea que se haga referencia a ella por una referencia de tipo de clase derivada. En otras palabras, puede abatir para revertir el efecto de upcasting anterior. Pero no puede tener un objeto de clase base referenciado por una referencia de un tipo de clase derivado.


El lenguaje C # no permite estos operadores, pero aún puede escribirlos y funcionan:

[System.Runtime.CompilerServices.SpecialName] public static Derived op_Implicit(Base a) { ... } [System.Runtime.CompilerServices.SpecialName] public static Derived op_Explicit(Base a) { ... }


Eso no puede funcionar. Ve a la página de ayuda vinculada por el error de compilación.

La mejor solución es usar métodos de fábrica aquí.


Esto no es posible porque ¿cómo vas a obtener el "extra" que tiene la clase derivada? ¿Cómo sabría el compilador que se refiere a derivedClass1 y no derivedClass2 cuando crea una instancia?

Creo que lo que realmente está buscando es el patrón de fábrica o similar para que pueda crear instancias de los objetos sin saber realmente el tipo explícito que se está instanciando. En su ejemplo, tener el método "Insertar" sería una interfaz de la instancia que la fábrica devuelve implementa.



No digo que recomiendo esto. Pero podría convertir la clase base en una cadena JSON y luego convertirla a la clase derivada.

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));


No profundamente, en idiomas "administrados". Esto es downcasting , y no hay una manera correcta de manejarlo, exactamente por la razón que usted describió (las subclases proporcionan más que clases base, ¿de dónde viene este "más"?). Si realmente desea un comportamiento similar para una jerarquía particular, puede usar constructores para los tipos derivados que tomarán el tipo base como un prototipo.

Se podría construir algo con la reflexión que maneja los casos simples (tipos más específicos que no tienen un estado de adición). En general, solo rediseña para evitar el problema.

Editar: Woops, no se pueden escribir operadores de conversión entre tipos base / derivados. Una rareza de Microsoft tratando de "protegerte" contra ti mismo. Ah, bueno, al menos no están tan mal como el sol.


No sé por qué nadie ha dicho esto y es posible que haya olvidado algo, pero puede usar la palabra clave como y si necesita hacer una instrucción if, use if.

SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass if(class is SomeDerivedClass) ;

-edit- hice esta pregunta hace mucho tiempo


No, esto no es posible En un lenguaje administrado como C #, simplemente no funcionará. El tiempo de ejecución no lo permitirá, incluso si el compilador lo deja pasar.

Usted mismo dijo que esto parece tonto:

SomeBaseClass class = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)class;

Entonces pregúntate, ¿la class es realmente una instancia de SomeDerivedClass ? No, entonces la conversión no tiene sentido. Si necesita convertir SomeBaseClass en SomeDerivedClass , debe proporcionar algún tipo de conversión, ya sea un constructor o un método de conversión.

Sin embargo, suena como si la jerarquía de su clase necesitara algo de trabajo. En general, no debería ser posible convertir una instancia de clase base en una instancia de clase derivada. En general, debe haber datos y / o funcionalidades que no se aplican a la clase base. Si la funcionalidad de la clase derivada se aplica a todas las instancias de la clase base, entonces debe ser acumulada en la clase base o incorporada en una nueva clase que no es parte de la jerarquía de la clase base.


Personalmente, no creo que valga la pena usar la herencia en este caso. En su lugar, simplemente pase la instancia de la clase base en el constructor y acceda a ella a través de una variable miembro.

private class ExtendedClass //: BaseClass - like to inherit but can''t { public readonly BaseClass bc = null; public ExtendedClass(BaseClass b) { this.bc = b; } public int ExtendedProperty { get { } } }


Recientemente he tenido la necesidad de extender un DTO simple con un tipo derivado para poner más propiedades en él. Luego quise volver a utilizar la lógica de conversión que tenía, desde los tipos de bases de datos internas hasta los DTO.

La forma en que lo resolví fue mediante la aplicación de un constructor vacío en las clases de DTO, usándolo así:

class InternalDbType { public string Name { get; set; } public DateTime Date { get; set; } // Many more properties here... } class SimpleDTO { public string Name { get; set; } // Many more properties here... } class ComplexDTO : SimpleDTO { public string Date { get; set; } } static class InternalDbTypeExtensions { public static TDto ToDto<TDto>(this InternalDbType obj) where TDto : SimpleDTO, new() { var dto = new TDto { Name = obj.Name } } }

Luego puedo reutilizar la lógica de conversión del DTO simple al convertir al complejo. Por supuesto, tendré que completar las propiedades del tipo complejo de alguna otra manera, pero con muchas, muchas propiedades del DTO simple, esto realmente simplifica las cosas de la OMI.


Sí, este es un olor codicioso, y básicamente esclarece el hecho de que su cadena de herencia está rota.

Mi suposición (de la muestra limitada) es que preferiría que DerivedClass opere en una instancia de SomeBaseClass, de modo que "DerivedClass tiene un SomeBaseClass", en lugar de "DerivedClass es un SomeBaseClass". Esto se conoce como "favorecer la composición sobre la herencia".