J’ai une classe de base Foo qui est concrète et contient 30 méthodes pertinentes pour ses sous-classes.
Maintenant, j’ai rencontré une situation spécifique à la classe de base et je veux créer une méthode qui ne peut pas être héritée, est-ce possible?
Class Foo { /* ... inheritable methods ... */ /* non-inheritable method */ public bool FooSpecificMethod() { return true; } } Class Bar : Foo { /* Bar specific methods */ } var bar = new Bar(); bar.FooSpecificMethod(); /* is there any way to get this to throw comstackr error */
MODIFIER
Je ne suis pas sûr d’avoir été clair à l’origine.
Je comprends les principes d’inheritance et le principe de substitution de Liskov. Dans ce cas, il y a une seule exception qui traite UNIQUEMENT du cas “non hérité” et je ne voulais donc pas créer de sous-classe “uninheritedFoo”.
Je demandais s’il est techniquement possible de créer une situation où foo.FooSpecificMethod () est une méthode valide et accessible au public, mais subclassoffoo.FooSpecificMethod () génère une erreur de compilation.
En gros, je veux une méthode scellée sur une classe non scellée.
Je repenserais la nécessité de cela.
Si vous utilisez l’inheritance, vous suggérez que “Bar” EST UN “Foo”. Si “Bar” est toujours un “Foo”, les méthodes qui fonctionnent sur “Foo” doivent également fonctionner sur “Bar”.
Si ce n’est pas le cas, je retravaillerais cela comme une méthode privée. Publiquement, Bar devrait toujours être un Foo.
Juste pour aller plus loin –
Si vous pouviez le faire, les choses deviendraient très compliquées. Vous pourriez avoir des situations où:
Foo myBar = new Bar(); // This is legal myBar.FooSpecificMethod(); // What should this do? // It's declared a Foo, but is acutally a Bar
Vous pouvez toutefois forcer ce comportement en utilisant la reflection. Je pense que c’est une mauvaise idée, mais FooSpecificMethod () pourrait en vérifier le type, et si ce n’est pas typeof (Foo), lève une exception. Ce serait très déroutant et aurait une très mauvaise odeur.
Modifier en réponse à la question de la question:
Il n’existe aucun moyen pour le compilateur d’appliquer ce que vous demandez. Si vous voulez vraiment forcer le compilateur à vérifier ceci et à l’empêcher, vous devriez vraiment envisager de faire de Foo une classe scellée. Vous pouvez utiliser d’autres méthodes d’extension que le sous-classement dans ce cas.
Par exemple, vous pouvez envisager d’utiliser des événements ou des delegates pour étendre le comportement au lieu de permettre à l’object d’être des sous-classes.
Essayer de faire ce que vous accomplissez, c’est essentiellement essayer d’empêcher les objectives principaux de l’inheritance.
Brian a raison sur Liskov Substitution (à la mode). Et Reed a raison sur “est-un” (également à la mode); en fait, ils vous disent tous les deux la même chose.
Vos méthodes publiques sont un contrat avec les utilisateurs de votre classe Foo, disant que “vous pouvez toujours” appeler ces méthodes sur Foo.
Sous-classe Foo signifie que vous dites qu’une sous-classe, par exemple Bar, est toujours acceptable si un Foo peut être utilisé. En particulier, cela signifie que vous n’hériterez pas (nécessairement) de l’implémentation de Foo (vous pouvez le remplacer, ou Foo peut être abstrait et ne pas donner d’implémentation particulière pour une méthode).
L’inheritance de la mise en œuvre (le cas échéant) est un détail; ce dont vous héritez réellement, c’est l’interface publique, le contrat, la promesse faite aux utilisateurs qu’un Bar peut être utilisé comme un Foo.
En effet, ils peuvent même ne jamais savoir qu’ils ont un Bar, pas un Foo: si je crée une FooFactory et que j’écris son Foo * getAFoo () pour renvoyer un pointeur sur un Bar, ils ne le sauront peut-être jamais et ne devront pas nécessairement .
Lorsque vous rompez ce contrat, vous cassez l’orientation object. (Et les classes de la collection Java, en lançant des exceptions NotSupported, coupent entièrement en fin d’object – les utilisateurs ne peuvent plus utiliser les soi-disant sous-classes de manière polymorphe. C’est une conception médiocre, qui a causé des maux de tête importants à beaucoup d’utilisateurs Java, pas quelque chose à imiter. )
S’il existe une méthode publique que les sous-classes Foo ne peuvent pas utiliser, cette méthode ne devrait pas figurer dans Foo, mais dans une sous-classe Foo, et les autres sous-classes devraient dériver de Foo.
Cela ne signifie PAS que toutes les méthodes de Foo doivent être appelables sur des sous-classes. Non, je ne me contredis pas. Les méthodes non publiques ne font pas partie de l’interface publique d’une classe.
Si vous voulez une méthode dans Foo qui ne puisse pas être appelée sur Bar, et ne devrait pas être publiquement appelable dans Foo, alors rendez cette méthode privée ou protégée.
Non, cela violerait le principe de substitution de Liskov .
De manière pragmatique, vous pouvez le faire “throw NotImplementedException ()” dans Bar, ou supprimer la méthode de Foo et la déplacer vers les sous-classes auxquelles elle s’applique.
Rendre la fonction privée l’empêchera d’être appelée directement par les sous-classes.
Si vous parlez de fonctions virtuelles que vous ne souhaitez pas surcharger, marquez la fonction comme étant scellée au point où vous souhaitez “verrouiller” la fonction.
Même s’il s’agit d’une fonction privée, elle pourrait toujours être appelée par reflection.
Vous pouvez également déclarer la fonction sur une interface et implémenter explicitement l’interface sur la classe, ce qui vous obligerait à la transtyper sur l’interface pour utiliser la fonction.
Vous pouvez créer une fonction privée, puis l’appeler en utilisant la reflection. Probablement un peu à la mer. Quoi qu’il en soit, il suffit de mettre la fonction dans votre classe de base, avec des commentaires indiquant qu’elle ne doit être appelée que depuis la classe de base. Peut-être même ces gentils /// commentaires qui apparaissent avec intellisense. Ensuite, vous pourriez avoir un bogue, mais bon, vous aurez toujours des bugs, et le mieux que vous puissiez faire est de documenter cette situation pour essayer de l’éviter.
La solution que j’ai finalement utilisée créait une classe héritée interne publique. De cette façon, il est capable d’accéder aux variables privées de la classe de base (comme il le faut) et je n’ai pas besoin d’exposer publiquement aucune de ces variables ou fonctions privées (ce que je ne souhaite pas).
Merci beaucoup pour tous vos conseils, cela m’a amené à repenser exactement ce dont j’avais besoin de cet arrangement.
Il y a deux raisons pour lesquelles une classe dérivée peut cacher des membres de la classe de base sans violer le principe de substitution de Liskov:
Je dirais que bien que cacher un membre de la classe de base soit souvent un signe de mauvaise conception, il y a des cas où cacher des membres de la classe de base serait plus approprié que de les laisser exposés; si une classe dérivée ne peut pas utiliser correctement une méthode protégée (comme ce serait le cas avec la méthode MemberwiseClone
disponible pour les dérivés de List
), il n’y a aucune raison de l’exposer. Malheureusement, il n’y a pas de moyen parfaitement propre de cacher de tels membres. Le mieux que l’on puisse faire est de déclarer un membre public du même nom (en utilisant un “nouveau” modificateur) qui bloquerait tout access au membre de la classe de base. Par exemple, code pourrait définir une classe statique publique nestede nommée MemberwiseClone
sans membres. L’atsortingbut EditorBrowseable
peut être utilisé pour empêcher Intellisense d’afficher cette classe nestede, mais même si le code essayait d’utiliser cette classe, il ne comportait aucun membre et il était impossible de créer un emplacement de stockage de ce type. Il devrait donc être relativement inoffensif.
Vous devez considérer Composition [ http://en.wikipedia.org/wiki/Composite_pattern } sur l’inheritance si vous souhaitez obtenir ce que vous tentez de faire et exposer des méthodes à partir de l’object privé (c’est-à-dire celui dont vous héritez actuellement).
Comme d’autres l’ont souligné, ce n’est pas possible et, surtout, cela ne devrait pas être nécessaire.
J’ai rencontré le même problème quand j’avais la structure suivante:
– Une classe parent avec des propriétés de classes enfants.
– Les classes enfants exposeraient des propriétés différentes, mais toutes avaient la même procédure d’initialisation, j’ai donc placé le code dans la classe parente.
public class Parent { public CHILD1 Child1; public CHILD2 Child2; public Parent(params args){ Child1 = new CHILD1(arg1, arg2); Child2 = new CHILD2(arg1, arg2); } //split here public Parent(object arg1, object arg2) { PropertyInfo[] list = this.GetType().GetProperties(); foreach (var pi in list) { pi.SetValue(this, BL.Method(arg1, arg2, this.GetType().Name, pi.Name) // get data } } } public class CHILD1 : Parent { public CHILD1(object arg1, object arg2) : base(arg1, arg2) { } public object C1Property1 { get; set; } public object C1Property2 { get; set; } // .. } public class CHILD2 : Parent { public CHILD2(object arg1, object arg2) : base(arg1, arg2) { } public object C2Property1 { get; set; } public object C2Property2 { get; set; } // .. }
Maintenant, je pourrais appeler parent.Child1.C1Property1
. Mais le code autoriserait également l’appel suivant: parent.Child1.Child1.Property1
, ce qui n’aurait aucun sens et donnerait lieu à une exception car la propriété Child1.Child1
serait toujours null
.
Pour résoudre ce problème, tout ce que je devais faire était de scinder le code de la classe parente en deux classes: