La résolution de la méthode « plus dérivée » dans un forçage virtuel
-
21-08-2019 - |
Question
J'ai une classe de base simple et classe dérivée:
class Base
{
public virtual void Write(int value)
{
Console.WriteLine("base int: {0}", value);
}
public virtual void Write(string value)
{
Console.WriteLine("base string: {0}", value);
}
}
class Derived : Base
{
public override void Write(int value)
{
Console.WriteLine("derived int: {0}", value);
}
public virtual void Write(IEnumerable enumerable)
{
Console.WriteLine("derived IEnumerable: {0}", enumerable);
}
public virtual void Write(object o)
{
Console.WriteLine("derived obj: {0}", o);
}
}
Si je lance ceci:
static void Main(string[] args)
{
Derived d = new Derived();
Console.WriteLine("derived:");
d.Write(42);
d.Write("hello");
Console.WriteLine("base:");
Base b = d;
b.Write(42);
b.Write("hello");
}
Je reçois:
derived:
derived obj: 42
derived IEnumerable: hello
base:
derived int: 42
base string: hello
Mais je me attends "b.Write (42)" et "d.Write (42)" identiques. La même chose pour le cas de la chaîne.
Qu'est-ce que je ne comprends? Comment puis-je obtenir le comportement d'être ce que je me attends donné la contrainte que je ne peux pas modifier « Base »?
UPDATE : Voir d'Eric après.
La solution
Cela se produit parce que C # considère les méthodes déclarées dans un type avant toute autre chose, y compris les méthodes de remplacement. Voir: Section 7.3 du C # spec .
explique assez bien et explique aussi les raisons.
Ce comportement très unintuitive se justifie par les deux règles suivantes:
- Si oui ou non une méthode est surchargée est un détail de mise en œuvre qui devraient être autorisés à changer sans casser le code client.
- Les modifications apportées à une classe de base qui ne cassent pas une classe héritée devrait ne cassera pas les clients du hérité classe.
Autres conseils
chaîne peut être implicitement à IEnumerable (de caractères), mais son ToString () retourne toujours la chaîne. Par conséquent, la résolution est b.Write("hello");
méthode virtuelle IEnumerable parce qu'il est plus proche du type référencé.
Je vérifierai, mais si vous remplacez la surcharge de chaîne dans votre classe dérivée, il pourrait résoudre correctement dans le code client.
EDIT
Je me trompais, primordiale ne permet pas. Vous pourriez avoir à renommer vos virtuals dérivées pour éviter la collision.
EDIT II
Le travail ci-dessous est, mais il est uber hackey, et je ne l'aime pas. Ajouter cette méthode à Derived
:
public new void Write(string value)
{
base.Write(value);
}