J’ai rencontré un problème avec la hiérarchie de mes classes, dans une application WPF. C’est l’un de ces problèmes où deux arbres d’inheritance fusionnent et que vous ne pouvez trouver aucun moyen logique de faire en sorte que votre inheritance fonctionne correctement sans inheritance multiple. Je me demande si quelqu’un a des idées shinyes pour faire fonctionner ce type de système, sans que cela soit impossible à suivre ou à déboguer.
Je suis un ingénieur de bas niveau, alors ma première pensée est toujours: “Oh! Je vais juste écrire quelques-uns de ces cours en C ++ natif et les référencer en externe! Ensuite, je pourrai m’amuser avec mon ancienne école!” Hélas, cela n’aide pas lorsque vous devez hériter de contrôles gérés …
Permettez-moi de montrer un extrait de mon diagramme de classes projeté actuel:
____________________________________ _____________________________________ | CustomizableObject | | System.Windows.Controls.UserControl | |____________________________________| |_____________________________________| | ssortingng XAMLHeader() | ▲ | ssortingng XAMLFooter() |◄--┐ | | CustomizableObject LoadObject() | \ | | | \ | |____________________________________| \ | ▲ ▲ \ | | | \ | | | \ | _________________ ______________________ \ _____________________ | SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | |_________________| |______________________| |_____________________| ▲ ▲ | | | | ________ _____________ | Sprite | | SpriteFrame | |________| |_____________|
Le problème est assez clair: la séparation des arborescences d’object CustomizableObject et CustomizableControl – et l’insertion de UserControl dans l’une des arborescences, mais pas les deux.
Il n’a aucun sens pratique de déplacer l’implémentation de CustomizableObject dans ses classes dérivées, car l’implémentation ne varie pas en fonction de la classe. En outre, il serait vraiment déroutant de le mettre en œuvre plusieurs fois. Donc, je ne veux vraiment pas faire de CustomizableObject une interface. La solution d’interface n’a aucun sens pour moi. (Pour être honnête, les interfaces n’ont jamais vraiment eu de sens.)
Alors je le répète, quelqu’un a des idées shinyes? Celui-ci est un vrai cornichon. J’aimerais en savoir plus sur la manière de faire fonctionner les interfaces AVEC mon arbre d’objects plutôt que contre lui. Je fabrique ce simple moteur de sprite en utilisant WPF et C # comme un exercice solide, plus que tout. C’est si facile à résoudre en C ++ – mais je dois trouver un moyen de résoudre ces problèmes dans un environnement géré, plutôt que de lever les arm en l’air et de revenir à Win32 dès que les choses se compliquent.
Une approche consiste à utiliser des méthodes d’extension avec une interface pour fournir votre implémentation de “classe dérivée”, un peu comme System.Linq.Queryable:
interface ICustomizableObject { ssortingng SomeProperty { get; } } public static class CustomizableObject { public static ssortingng GetXamlHeader(this ICustomizableObject obj) { return DoSomethingWith(obj.SomeProperty); } // etc } public class CustomizableControl : System.Windows.Controls.UserControl, ICustomizableObject { public ssortingng SomeProperty { get { return "Whatever"; } } }
Utilisation: Tant que vous avez une directive using pour (ou que vous êtes dans le même espace de noms que) l’espace de noms dans lequel vos méthodes d’extension sont définies:
var cc = new CustomizableControl(); var header = cc.GetXamlHeader();
Vous avez deux options ici; utiliser des interfaces, ou utiliser une composition. Honnêtement, les interfaces sont très puissantes, et après avoir lu cette ligne
La solution d’interface n’a aucun sens pour moi. (Pour être honnête, les interfaces n’ont jamais vraiment eu de sens.)
Je pense que vous devriez apprendre à les utiliser correctement. Cela dit, s’il existe simplement une logique dont plusieurs classes ont besoin, mais qu’il ne soit pas logique que ces classes héritent de la même classe de base, créez simplement une classe pour encapsuler cette logique et ajoutez une variable membre de cette classe à vos classes. qui vous donnent des problèmes. De cette façon, toutes les classes contiennent la logique mais peuvent être séparées dans leurs hiérarchies d’inheritance. Si les classes doivent implémenter une interface commune, utilisez les interfaces.
Je regarde cela, et CustomizableObject
est en train de crier pour devenir une interface (et puisque chaque type concret est convertible en object, cette partie du nom est redondante). Le problème que vous rencontrez est que vous ne savez pas comment conserver une logique de base qui sera partagée ou ne varie que légèrement selon l’implémentation, et vous souhaitez stocker cette logique dans l’arborescence elle-même afin qu’elle fonctionne de manière polymorphe (is qu’un mot?).
Vous pouvez y parvenir grâce aux delegates. Je ne sais pas exactement quels membres vous causent des problèmes, mais peut-être quelque chose de plus semblable à ceci:
____________________________________ _____________________________________ | ICustomizable | | System.Windows.Controls.UserControl | | | |_____________________________________| | Func XAMLHeader; | ▲ | Func XAMLFooter |◄--┐ | | ICustomizabl LoadObject() | \ | | | \ | |____________________________________| \ | ▲ ▲ \ | | | \ | | | \ | _________________ ______________________ \ _____________________ | SpriteAnimation | | SpriteAnimationFrame | └---| CustomizableControl | |_________________| |______________________| |_____________________| ▲ ▲ | | | | ________ _____________ | Sprite | | SpriteFrame | |________| |_____________|
En outre, vous avez probablement une logique réellement statique qui, à votre avis, appartient vraiment à votre type CustomizableObject. Mais ceci est probablement faux: vous avez construit le type dans l’intention de l’utiliser dans une situation donnée. Par exemple, dans le contexte, il semble que vous créiez ces contrôles et animations et que vous les utilisiez sous Windows Form. La chose à faire est d’avoir votre propre formulaire qui hérite de la base System.Windows.Form, et ce nouveau type de formulaire doit connaître ICustomizableObject et son utilisation. C’est là que va aller votre logique statique.
Cela semble un peu gênant, mais sa précision est avérée lorsque vous décidez de changer de moteur de présentation. Que se passe-t-il si vous portez ce code sur WPF ou Silverlight? Ils devront probablement utiliser votre code d’implémentation un peu différemment de Windows Forms et vous devrez probablement modifier vos implémentations CustomizableControl. Mais votre logique statique est maintenant parfaitement au bon endroit.
Enfin, la méthode LoadObject () que vous utilisez me semble également au mauvais endroit. Vous dites que vous voulez que chaque type personnalisable fournisse une méthode que vous pouvez appeler et qui sait se charger / se construire. Mais c’est vraiment quelque chose de différent. Vous voudrez peut-être une autre interface nommée IConstructable
et que votre type ICustomizable l’implémente ( IConstructable
).
On dirait que vous devrez recourir à des interfaces et à la composition d’objects.
J’utilise des mixins dans un tel cas. Les mixins sont un concept puissant utilisé dans de nombreuses langues pour append des fonctionnalités à une classe lors de l’exécution. les mixins sont bien connus dans de nombreuses langues. Le framework de re-mix est un framework qui apporte la technologie mixin à .NET.
L’idée est simple: décorez votre classe avec un atsortingbut de classe qui représente un mixin. Au moment de l’exécution, cette fonctionnalité sera ajoutée à votre classe. Vous pouvez donc émuler plusieurs inheritances.
La solution à votre problème pourrait être de transformer CustomizableObject en mixin. Vous aurez besoin du framework de re-mix pour cela.
[Utilisations (CustomizableObjectMixin)] CustomizableControl: UserControl
S’il vous plaît vérifier remix.codeplex.com pour plus de détails.
Je pense est une solution.
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() { } }
Classe MultipleInheritance ajoute un nouveau comportement à deux objects différents sans affecter le comportement de ces objects. MultipleInheritance a les mêmes comportements que les objects livrés (hérite des deux comportements).