interface IBase
{
string Name { get; }
}
class Base : IBase
{
public Base() => this.Name = "Base";
public string Name { get; }
}
class Derived : Base//, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
class Program
{
static void Main(string[] args)
{
IBase o = new Derived();
Console.WriteLine(o.Name);
}
}
Dans ce cas, la sortie sera "Base".
Si je déclare explicitement que Derived implémente IBase (qui est en fait déjà implémenté par la classe de base Base et que cette annotation semble inutile), le résultat sera "Derived"
class Derived : Base, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
Quelle est la raison d'un tel comportement?
VS 15.3.5, C # 7
Cela est expliqué dans les sections 13.4.4 à 13.4.6 de la spécification C # 5. Les sections pertinentes sont citées ci-dessous, mais si vous indiquez explicitement qu'une classe implémente une interface, elle déclenche à nouveau le mappage d'interface. Le compilateur prend donc la classe that pour déterminer quelle implémentation chaque membre d'interface est. orienté vers.
13.4.4 Mappage d'interface
Une classe ou une structure doit fournir les implémentations de tous les membres des interfaces répertoriées dans la liste de classes de base de la classe ou de la structure. Le processus de localisation des implémentations des membres d'interface dans une classe ou une structure d'implémentation est appelé mappage d'interface.
Le mappage d'interface pour une classe ou une structure
C
localise une implémentation pour chaque membre de chaque interface spécifiée dans la liste de classes de base deC
. L'implémentation d'un membre d'interface particulierI.M
, oùI
est l'interface dans laquelle le membreM
est déclaré, est déterminée en examinant chaque classe ou structS
, en commençant parC
et en répétant pour chaque classe de base successive deC
, jusqu'à ce qu'une correspondance soit trouvée. :
- Si
S
contient une déclaration d'une implémentation de membre d'interface explicite qui correspond àI
etM
, alors ce membre est l'implémentation deI.M
.- Sinon, si
S
contient une déclaration d'un membre public non statique correspondant à M, alors ce membre est l'implémentation deI.M
. Si plusieurs membres correspondent, le membre correspondant à l'implémentation deI.M
n'est pas spécifié. Cette situation ne peut se produire que siS
est un type construit dans lequel les deux membres déclarés dans le type générique ont des signatures différentes, mais les arguments de type rendent leurs signatures identiques....
13.4.5 Héritage d'implémentation d'interface
Une classe hérite de toutes les implémentations d'interface fournies par ses classes de base . Sans réimplémenter explicitement une interface, une classe dérivée ne peut en aucun cas modifier les mappages d'interface hérités de ses classes de base. Par exemple, dans les déclarations
interface IControl { void Paint(); } class Control: IControl { public void Paint() {...} } class TextBox: Control { new public void Paint() {...} }
la méthode
Paint
dansTextBox
masque la méthodePaint
dansControl
, mais ne modifie pas le mappage deControl.Paint
surIControl.Paint
, et les appels àPaint
par le biais d'instances de classe et d'interface auront les effets suivantsControl c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();
...
13.4.6 Réimplémentation d'interface
Une classe qui hérite d'une implémentation d'interface est autorisée à réimplémenter l'interface en l'incluant dans la liste de classes de base.
Une réimplémentation d'une interface suit exactement les mêmes règles de mappage d'interface qu'une implémentation initiale d'une interface. Ainsi, le mappage d'interface hérité n'a aucun effet sur le mappage d'interface établi pour la réimplémentation de l'interface. Par exemple, dans les déclarations
interface IControl { void Paint(); } class Control: IControl { void IControl.Paint() {...} } class MyControl: Control, IControl { public void Paint() {} }
le fait que
Control
mappeIControl.Paint
surControl.IControl.Paint
n’affecte pas la nouvelle implémentation dansMyControl
, qui mappeIControl.Paint
surMyControl.Paint
.
Si Derived
n'implémente pas IBase
et déclare new string Name
, cela signifie que Derived.Name
et IBase.Name
ne sont pas identiques logiquement. Ainsi, lorsque vous accédez à IBase.Name
, il le recherche dans la classe Base
, en implémentant IBase
. Si vous supprimez la propriété new string Name
, la sortie sera Derived
, car maintenant Derived.Name
= Base.Name
= IBase.Name
. Si vous implémentez explicitement IBase
, le résultat sera Derived
, car Derived.Name
= IBase.Name
. Si vous transformez o
en Derived
, le résultat sera Derived
, car vous accédez maintenant à Derived.Name
au lieu de IBase.Name
.