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);
}
}
この場合、出力は「ベース」になります。
DerivedがIBaseを実装していることを明示的に述べた場合(これは実際には基本クラスBaseによって既に実装されており、そのような注釈は役に立たないようです)、出力は "Derived"になります
class Derived : Base, IBase
{
public Derived() => this.Name = "Derived";
public new string Name { get; }
}
そのような振る舞いの理由は何ですか?
VS 15.3.5、C#7
C#5仕様のセクション13.4.4から13.4.6で説明されています。関連するセクションを以下に示しますが、基本的に、クラスがインターフェースを実装すると明示的に述べた場合、インターフェースマッピングが再度トリガーされるため、コンパイラーはthatクラスを取得します各インターフェイスメンバーがどの実装にマップされるかを決定するために使用するものとして。
13.4.4インターフェイスマッピング
クラスまたは構造体は、クラスまたは構造体の基本クラスリストにリストされているインターフェイスのすべてのメンバーの実装を提供する必要があります。実装クラスまたは構造体でインターフェースメンバーの実装を見つけるプロセスは、インターフェースマッピングと呼ばれます。
クラスまたは構造体
C
のインターフェイスマッピングは、C
の基本クラスリストで指定された各インターフェイスの各メンバーの実装を検索します。特定のインターフェイスメンバーI.M
の実装(I
はメンバーM
が宣言されているインターフェイス)は、各クラスまたはstructS
を調べることによって決定されます、C
で始まり、C
の連続する基本クラスごとに、一致が見つかるまで繰り返します。
S
に、I
およびM
と一致する明示的なインターフェイスメンバー実装の宣言が含まれている場合、このメンバーはI.M
の実装です。- それ以外の場合、
S
にMと一致する非静的パブリックメンバーの宣言が含まれていると、このメンバーはI.M
の実装になります。複数のメンバーが一致する場合、どのメンバーがI.M
の実装であるかは不明です。この状況が発生する可能性があるのは、S
が、ジェネリック型で宣言された2つのメンバーのシグネチャが異なる構築型であるが、型引数によってシグネチャが同一である場合のみです。...
13.4.5インターフェース実装の継承
クラスは、その基本クラスによって提供されるすべてのインターフェース実装を継承します。インターフェイスを明示的に再実装しない限り、派生クラスは、基本クラスから継承するインターフェイスマッピングを変更することはできません。たとえば、宣言では
interface IControl { void Paint(); } class Control: IControl { public void Paint() {...} } class TextBox: Control { new public void Paint() {...} }
Paint
のTextBox
メソッドはPaint
のControl
メソッドを非表示にしますただし、Control.Paint
のマッピングは変更されませんIControl.Paint
、およびクラスインスタンスとインターフェースインスタンスを介したPaint
の呼び出しには、次の効果があります。Control 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インターフェースの再実装
インターフェイス実装を継承するクラスは、基本クラスリストに含めることにより、インターフェイスを再実装できます。
インターフェイスの再実装は、インターフェイスの初期実装とまったく同じインターフェイスマッピングルールに従います。したがって、継承されたインターフェイスマッピングは、インターフェイスの再実装のために確立されたインターフェイスマッピングにはまったく影響しません。たとえば、宣言では
interface IControl { void Paint(); } class Control: IControl { void IControl.Paint() {...} } class MyControl: Control, IControl { public void Paint() {} }
Control
がIControl.Paint
をControl.IControl.Paint
にマップするという事実は、MyControl
の再実装には影響せず、IControl.Paint
をMyControl.Paint
にマップします。 。
Derived
がIBase
を実装せず、new string Name
を宣言する場合、Derived.Name
とIBase.Name
は論理的に同じではないことを意味します。したがって、IBase.Name
にアクセスすると、Base
クラスでIBase
を実装してそれを検索します。 new string Name
プロパティを削除すると、Derived.Name
= Base.Name
= IBase.Name
になるため、出力はDerived
になります。 IBase
を明示的に実装すると、Derived.Name
= IBase.Name
になるため、出力はDerived
になります。 o
をDerived
にキャストすると、Derived.Name
ではなくIBase.Name
にアクセスしているため、出力はDerived
になります。