Redéfinition vs cacher la méthode [double]
-
27-09-2019 - |
Question
Cette question a déjà une réponse ici:
Je suis un peu confus sur le remplacement par rapport à cacher une méthode en C #. utilisations pratiques de chacun seraient également appréciés, ainsi qu'une explication pour lorsque on utiliserait chacun.
Je suis confus au sujet primordial - pourquoi nous remplaçons? Ce que j'ai appris est jusqu'à présent que par overring nous pouvons fournir la mise en oeuvre désirée à une méthode d'une classe dérivée, sans changer la signature.
Si je ne l'emporte pas sur la méthode de la superclasse et je fais des changements à la méthode de la sous-classe, cela fera des changements à la méthode super classe?
Je suis aussi confus au sujet de ce qui suit - qu'est-ce que cela démontre
class A
{
virtual m1()
{
console.writeline("Bye to all");
}
}
class B : A
{
override m1()
{
console.writeLine("Hi to all");
}
}
class C
{
A a = new A();
B b = new B();
a = b; (what is this)
a.m1(); // what this will print and why?
b = a; // what happens here?
}
La solution
Considérez:
public class BaseClass
{
public void WriteNum()
{
Console.WriteLine(12);
}
public virtual void WriteStr()
{
Console.WriteLine("abc");
}
}
public class DerivedClass : BaseClass
{
public new void WriteNum()
{
Console.WriteLine(42);
}
public override void WriteStr()
{
Console.WriteLine("xyz");
}
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();
isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz
Redéfinition est la voie OO classique dans lequel une classe dérivée peut avoir un comportement plus spécifique qu'une classe de base (dans certaines langues que vous avez pas d'autre choix que de le faire). Quand une méthode virtuelle est appelée sur un objet, la version la plus dérivée de la méthode est appelée. Par conséquent, même si nous avons affaire à isReallyDerived
comme BaseClass
alors la fonctionnalité définie dans DerivedClass
est utilisé.
moyens de Masquage que nous avons une méthode complètement différente. Lorsque nous appelons WriteNum()
sur isReallyDerived
alors il n'y a aucun moyen de savoir qu'il ya un autre WriteNum()
sur DerivedClass
il est donc pas appelé. Il ne peut être appelée lorsque nous avons affaire à l'objet comme DerivedClass
.
La plupart du temps caché est mauvais. En règle générale, que ce soit, vous devriez avoir une méthode aussi virtuelle si elle susceptible d'être modifiée dans une classe dérivée, et le remplacer dans la classe dérivée. Il y a cependant deux choses, il est utile pour:
-
Compatibilité avant. Si
DerivedClass
avait une méthode deDoStuff()
, puis plus tardBaseClass
a été modifié pour ajouter une méthode deDoStuff()
, (rappelez-vous qu'ils peuvent être écrits par des personnes différentes et existent dans différentes assemblées), puis une interdiction de cacher membre aurait soudainement fait buggyDerivedClass
sans elle en changeant. En outre, si le nouveauDoStuff()
surBaseClass
était virtuelle, puis faire automatiquement surDerivedClass
un remplacement de celui-ci pourrait conduire à la méthode pré-existante appelée quand il ne devrait pas. Par conséquent, il est bon que la clandestinité est la valeur par défaut (nous utilisonsnew
pour le rendre clair que nous voulons absolument cacher, mais en laissant des peaux et émet un avertissement sur la compilation). -
covariance Poor man. Considérons une méthode
Clone()
surBaseClass
qui retourne une nouvelleBaseClass
qui est une copie de celui créé. Dans la dérogation surDerivedClass
cela va créer unDerivedClass
mais revenir commeBaseClass
, ce qui est aussi utile. Ce que nous pourrions faire est d'avoir unCreateClone()
virtuel protégé qui est surchargée. EnBaseClass
nous avons unClone()
qui renvoie le résultat de ce - et tout va bien - enDerivedClass
nous cacher cela avec une nouvelleClone()
qui retourne uneDerivedClass
. AppelClone()
surBaseClass
renvoie toujours une référenceBaseClass
, qui sera une valeurBaseClass
ou une valeur deDerivedClass
selon le cas. AppelClone()
surDerivedClass
retournera une valeurDerivedClass
, qui est ce que nous voudrions dans ce contexte. Il existe d'autres variantes de ce principe, mais il convient de noter qu'ils sont assez rares.
Une chose importante à noter le second cas, est que nous avons utilisé précisément pour cacher supprimer des surprises au code appelant, comme la personne qui utilise DerivedClass
peut légitimement attendre son Clone()
retourner un DerivedClass
. Les résultats de l'une des façons il pourrait être appelé sont maintenus cohérents entre eux. La plupart des cas de cacher des surprises présentant des risques, ce qui explique pourquoi ils sont généralement mal vus. Celui-ci se justifie précisément parce qu'elle permet de résoudre le problème même qui cache souvent présente.
En tout, caché est parfois nécessaire, souvent utile, mais généralement mauvais, alors soyez très prudent de celui-ci.
Autres conseils
est Redéfinition lorsque vous fournissez une nouvelle implémentation de override
d'une méthode dans une classe descendante lorsque cette méthode est définie dans la classe de base comme virtual
.
Hiding est lorsque vous fournissez une nouvelle mise en œuvre d'une méthode dans une classe descendante lorsque cette méthode est pas défini dans la classe de base comme virtual
, ou lorsque votre nouvelle implémentation ne précise pas override
.
Hiding est très souvent mauvaise; vous devriez essayer généralement de ne pas le faire si vous pouvez l'éviter à tout. Masquage peut provoquer des choses inattendues se produire, parce que les méthodes cachées ne sont utilisés que lorsqu'ils sont appelés à une variable du type réel que vous avez défini, pas si vous utilisez une référence de classe de base ... d'autre part, les méthodes virtuelles qui sont ignorées finiront par la bonne version méthode appelée, même lorsqu'il est appelé en utilisant la référence de classe de base sur une classe enfant.
Par exemple, considérons ces classes:
public class BaseClass
{
public virtual void Method1() //Virtual method
{
Console.WriteLine("Running BaseClass Method1");
}
public void Method2() //Not a virtual method
{
Console.WriteLine("Running BaseClass Method2");
}
}
public class InheritedClass : BaseClass
{
public override void Method1() //Overriding the base virtual method.
{
Console.WriteLine("Running InheritedClass Method1");
}
public new void Method2() //Can't override the base method; must 'new' it.
{
Console.WriteLine("Running InheritedClass Method2");
}
}
L'appel Let comme ça, avec une instance de InheritedClass, dans une référence correspondant:
InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();
Ce rendement ce que vous devez attendre; les deux méthodes disent qu'ils sont en cours d'exécution les versions InheritedClass.
Running InheritedClass Method1
Exécution InheritedClass Method2
Ce code crée une instance du même, InheritedClass, mais stocke dans une référence BaseClass:
BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();
Normalement, en vertu des principes de la POO, vous devriez vous attendre le même résultat que l'exemple ci-dessus. Mais vous ne recevez pas la même sortie:
Running InheritedClass Method1
Exécution BaseClass Method2
Lorsque vous avez écrit le code InheritedClass, vous avez sans doute voulu tous les appels à Method2()
pour exécuter le code que vous avez écrit en elle. Normalement, ce serait comment cela fonctionne - en supposant que vous travaillez avec une méthode de virtual
que vous avez remplacé. Mais parce que vous utilisez une méthode new
/ caché, il appelle la version sur la référence que vous utilisez, à la place.
Si tel est le comportement que vous voulez vraiment , puis; Voilà. Mais je suggère fortement que si c'est ce que vous voulez, il peut y avoir un plus grand problème d'architecture avec le code.
méthode redéfinie est prioritaire simpley une implémentation par défaut d'une méthode de la classe de base dans la classe dérivée.
Méthode Hiding: Vous pouvez utiliser mot-clé « nouveau » avant une méthode virtuelle dans une classe dérivée
class Foo
{
public virtual void foo1()
{
}
}
class Bar:Foo
{
public new virtual void foo1()
{
}
}
si vous faites une autre classe Bar1 qui est dérivé de Bar, vous pouvez remplacer foo1 qui est defind dans la barre.