simple - no implementa el miembro abstracto heredado c#
¿Cuáles son algunas buenas alternativas a la herencia múltiple en.NET? (6)
Me he encontrado con un pequeño problema con mi jerarquía de clases, en una aplicación WPF. Es uno de esos problemas en los que tienes dos árboles de herencia fusionados, y no puedes encontrar ninguna forma lógica de hacer que tu herencia funcione sin problemas sin herencia múltiple. Me pregunto si alguien tiene ideas brillantes para que funcione este tipo de sistema, sin que sea imposible seguirlo o depurarlo.
Soy un ingeniero de bajo nivel, así que lo primero que pienso es: "¡Oh, escribiré algunas de estas clases en C ++ nativo y las referiré externamente! ¡Entonces puedo tener toda mi diversión de OO de la vieja escuela!" Por desgracia, esto no ayuda cuando necesitas heredar de los controles administrados ...
Permítanme mostrar un fragmento de mi diagrama de clase proyectado actual:
____________________________________ _____________________________________
| CustomizableObject | | System.Windows.Controls.UserControl |
|____________________________________| |_____________________________________|
| string XAMLHeader() | ▲
| string XAMLFooter() |◄--┐ |
| CustomizableObject LoadObject() | / |
| <Possible other implementations> | / |
|____________________________________| / |
▲ ▲ / |
| | / |
| | / |
_________________ ______________________ / _____________________
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl |
|_________________| |______________________| |_____________________|
▲ ▲
| |
| |
________ _____________
| Sprite | | SpriteFrame |
|________| |_____________|
El problema es bastante claro: la separación de los árboles de objetos CustomizableObject y CustomizableControl --- y la inserción de UserControl en uno, pero no en ambos, de los árboles.
No tiene sentido práctico mover la implementación de CustomizableObject a sus clases derivadas, ya que la implementación no varía según la clase. Además, sería realmente confuso tenerlo implementado muchas veces. Entonces realmente no quiero hacer de CustomizableObject una interfaz. La solución de interfaz no tiene ningún sentido para mí. (Las interfaces nunca tuvieron mucho sentido para mí, para ser sincero ...)
Entonces vuelvo a decir, ¿alguien tiene ideas brillantes? Este es un verdadero pepinillo. Me gustaría obtener más información sobre cómo hacer que las interfaces funcionen CON mi árbol de objetos, en lugar de hacerlo en contra de él. Estoy haciendo este motor de sprite simple usando WPF y C # como un ejercicio sólido, más que nada. Esto es tan fácil de resolver en C ++, pero necesito resolver cómo resolver estos problemas en un entorno administrado, en lugar de lanzar las manos al aire y volver corriendo a Win32 cada vez que las cosas se ponen difíciles.
Creo que es uno de solución.
public interface IClassA
{
void Foo1();
void Foo2();
}
public interface IClassB
{
void Foo3();
void Foo4();
}
public class ClassA :IClassA
{
#region IClassA Members
public void Foo1()
{
}
public void Foo2()
{
}
#endregion
}
public class ClassB :IClassB
{
#region IClassB Members
public void Foo3()
{
}
public void Foo4()
{
}
#endregion
}
public class MultipleInheritance :IClassA, IClassB
{
private IClassA _classA;
private IClassB _classB;
public MultipleInheritance(IClassA classA, IClassB classB)
{
_classA = classA;
_classB = classB;
}
public void Foo1()
{
_classA.Foo1();
}
public void Foo2()
{
_classA.Foo2();
AddedBehavior1();
}
public void Foo3()
{
_classB.Foo3();
AddedBehavior2();
}
public void Foo4()
{
_classB.Foo4();
}
private void AddedBehavior1()
{
}
private void AddedBehavior2()
{
}
}
La herencia múltiple de clase agrega un nuevo comportamiento a dos objetos diferentes sin afectar el comportamiento de estos objetos. MultipleHealth tiene los mismos comportamientos que los objetos entregados (Hereda ambos comportamientos).
Estoy viendo esto, y CustomizableObject
simplemente está gritando para convertirse en una interfaz (y dado que cada tipo concreto es convertible a objeto, esa parte del nombre es redundante). El problema con el que se está metiendo es que no está seguro de cómo conservar una lógica básica que se compartirá o solo variará levemente según la implementación, y desea almacenar esta lógica en el árbol para que funcione polimórficamente (es esa una palabra?).
Puedes lograr esto a través de los delegados. No estoy seguro exactamente qué miembros te están causando problemas, pero quizás algo más como esto:
____________________________________ _____________________________________
| ICustomizable | | System.Windows.Controls.UserControl |
| | |_____________________________________|
| Func<string> XAMLHeader; | ▲
| Func<string> XAMLFooter |◄--┐ |
| ICustomizabl LoadObject() | / |
| <Possible other implementations> | / |
|____________________________________| / |
▲ ▲ / |
| | / |
| | / |
_________________ ______________________ / _____________________
| SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl |
|_________________| |______________________| |_____________________|
▲ ▲
| |
| |
________ _____________
| Sprite | | SpriteFrame |
|________| |_____________|
Además, es probable que tengas una lógica que sea genuinamente estática y que realmente sientas que pertenece a tu tipo de CustomizableObject. Pero esto probablemente sea falso: construiste el tipo con la intención de usar ese tipo en una situación específica. Por ejemplo, desde el contexto, parece que creará estos controles y animaciones y los usará en Windows Form. Lo que hay que hacer es tener su propia forma que herede de la base System.Windows.Form, y este nuevo tipo de formulario debe saber sobre ICustomizableObject y cómo usarlo. Ahí es donde irá tu lógica estática.
Esto parece un poco incómodo, pero se ha demostrado que es preciso cuando decide cambiar los motores de presentación. ¿Qué sucede si transfieres este código a WPF o Silverlight? Es probable que necesiten usar su código de implementación de forma un poco diferente a lo que haría Windows Forms, y es probable que necesite cambiar sus implementaciones de CustomizableControl. Pero su lógica estática ahora está exactamente en el lugar correcto.
Finalmente, el método LoadObject () que está usando también se destaca en el lugar equivocado. Usted está diciendo que quiere que cada tipo de Personalizable proporcione un método al que pueda llamar que sepa cómo cargarse / construirse. Pero esto es realmente algo diferente. Es posible que desee otra interfaz llamada IConstructable<T>
y que su tipo de letra personalizable implemente eso ( IConstructable<ICustomizable>
).
Parece que tendrá que recurrir a interfaces y composición de objetos.
Tienes dos opciones aquí; usar interfaces, o usar composición. Honestamente, las interfaces son muy poderosas, y después de leer esta línea
La solución de interfaz no tiene ningún sentido para mí. (Las interfaces nunca tuvieron mucho sentido para mí, para ser sincero ...)
Creo que deberías aprender a usarlos correctamente. Dicho esto, si simplemente existe alguna lógica que necesita una clase múltiple, pero no tiene sentido que estas clases hereden de la misma clase base, simplemente cree una clase para encapsular esa lógica y agregue una variable miembro de esa clase a sus clases que te están dando problemas De esta forma, todas las clases contienen la lógica pero pueden estar separadas en sus jerarquías de herencia. Si las clases deben implementar una (s) interfaz (s) común (s), entonces usa interfaces.
Un enfoque es usar métodos de extensión con una interfaz para proporcionar su implementación de "clase derivada", al igual que System.Linq.Queryable:
interface ICustomizableObject
{
string SomeProperty { get; }
}
public static class CustomizableObject
{
public static string GetXamlHeader(this ICustomizableObject obj)
{
return DoSomethingWith(obj.SomeProperty);
}
// etc
}
public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject
{
public string SomeProperty { get { return "Whatever"; } }
}
Uso: Siempre que tenga una directiva de uso para (o se encuentre en el mismo espacio de nombres) el espacio de nombres donde se definen sus métodos de extensión:
var cc = new CustomizableControl();
var header = cc.GetXamlHeader();
Yo uso mixins en tal caso. mixins es un poderoso concepto utilizado en muchos idiomas para agregar funcionalidad a una clase en tiempo de ejecución. mixins son bien conocidos en muchos idiomas. El marco de remezcla es un marco que lleva la tecnología mixin a .NET.
La idea es simple: decora tu clase con un atributo de clase que representa un mixin. En tiempo de ejecución, esta funcionalidad se agregará a su clase. Entonces puedes emular herencia múltiple.
La solución a su problema podría ser hacer CustomizableObject a mixin. Necesitarás el marco de remezcla para esto.
[Usos (CustomizableObjectMixin)] CustomizableControl: UserControl
Por favor revisa remix.codeplex.com para más detalles.