web-dev-qa-db-ja.com

派生クラスを基本クラスの実装インターフェースとして明示的にマーク

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

23
yevgenijz

C#5仕様のセクション13.4.4から13.4.6で説明されています。関連するセクションを以下に示しますが、基本的に、クラスがインターフェースを実装すると明示的に述べた場合、インターフェースマッピングが再度トリガーされるため、コンパイラーはthatクラスを取得します各インターフェイスメンバーがどの実装にマップされるかを決定するために使用するものとして。

13.4.4インターフェイスマッピング

クラスまたは構造体は、クラスまたは構造体の基本クラスリストにリストされているインターフェイスのすべてのメンバーの実装を提供する必要があります。実装クラスまたは構造体でインターフェースメンバーの実装を見つけるプロセスは、インターフェースマッピングと呼ばれます。

クラスまたは構造体Cのインターフェイスマッピングは、Cの基本クラスリストで指定された各インターフェイスの各メンバーの実装を検索します。特定のインターフェイスメンバーI.Mの実装(IはメンバーMが宣言されているインターフェイス)は、各クラスまたはstruct Sを調べることによって決定されます、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() {...}
}

PaintTextBoxメソッドはPaintControlメソッドを非表示にしますただし、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() {}
}

ControlIControl.PaintControl.IControl.Paintにマップするという事実は、MyControlの再実装には影響せず、IControl.PaintMyControl.Paintにマップします。 。

20
Jon Skeet

DerivedIBaseを実装せず、new string Nameを宣言する場合、Derived.NameIBase.Nameは論理的に同じではないことを意味します。したがって、IBase.Nameにアクセスすると、BaseクラスでIBaseを実装してそれを検索します。 new string Nameプロパティを削除すると、Derived.Name = Base.Name = IBase.Nameになるため、出力はDerivedになります。 IBaseを明示的に実装すると、Derived.Name = IBase.Nameになるため、出力はDerivedになります。 oDerivedにキャストすると、Derived.NameではなくIBase.Nameにアクセスしているため、出力はDerivedになります。

1