5

次のコードを検討してください。

class ChildClass : BaseClass {

    public void Method1() {} //some other method

}

abstract class BaseClass : IChildInterface {

    public
    virtual //<- If we add virtual so that this method can be overridden by ChildClass, we get StackOverflowException and DoWork() implementation in IChildInterface is never called.
    void DoWork() {
     //base class specific implmentation
     ((IChildInterface)this).DoWork(); //call into default implementation provided by IChildInterface
    }

}

interface IChildInterface : IBaseInterface {

    void IBaseInterface.DoWork() {
     //implmentation
    }

}

interface IBaseInterface {

    void DoWork();

}

問題は、子クラスによってオーバーライドできるようDoWork()BaseClassasをマークすると、のデフォルトの実装を呼び出すことができなくなり、.virtualIChildInterfaceDoWork()StackOverflowException

virtualから修飾子を削除するDoWork()BaseClass、すべてが機能し、IChildInterfaceのデフォルトの実装DoWork() が呼び出されます。

そのような動作はバグですか、それとも仕様ですか?

一部の子クラスが の独自の実装を提供できるようにする方法はありますかDoWork()(したがって、 の実装をオーバーライドしますBaseClass) が、のデフォルトの実装を引き続き使用でき ますか?IChildInterfaceDoWork()

4

3 に答える 3

4

再帰的に呼び出しBaseClass.DoWorkているため、運が良ければ StackOverflowException が発生します。呼び出しがメソッドの最後の呼び出しである場合、末尾呼び出しの最適化により無限再帰が発生します。アプリを強制終了するまで、コアが 100% で停止することになります。

このコード:

public virtual void DoWork() {
   ((IChildInterface)this).DoWork(); by IChildInterface
}

と同じです:

//That's the actual implementation of the interface method
public virtual void DoWork() {
     DoWork(); 
}

キーワードはvirtual問いません。それがなくても、無限再帰が得られます。存在するかどうかにかかわらず、この行はしばらくすると StackOverflowException をスローします。

new ChildClass().DoWork();

実装BaseClass.DoWorkすると、子クラスによってオーバーライドされない限り、誰もが利用できる単一の実装になりました。

インターフェイスは、C# 8 でも抽象クラスではありません。既定のメソッドの実装は、実際のメソッドではありません。名前が示すように、これはデフォルトの実装です。より良い実装が利用できない場合に使用されます。メソッドが既にクラスに実装されている場合、既定の実装を呼び出すことはできません。

実際、ほとんどの場合、デフォルトのメソッドが呼び出されるとは思わないでしょう。DIM は、明示的なインターフェイスの実装が使用されるのと同じ方法で、インターフェイスを介して明示的に呼び出されます。メソッドの呼び出し元は、基本レベルまたは中間レベルの実装ではなく、最も派生した実装が実行されることを期待しています。

さらに、以前の C# バージョンでも、実際に呼び出されるメソッドを変更するためにインターフェイスにキャストすることは期待できません。クラスでのみそれを期待するでしょう。基本クラスの実装を呼び出すには、baseキーワードを使用します。BaseClassただしの基本クラスは、メソッドObjectを持たないクラスです。DoWork

使用した場合:

void DoWork() {
    base.DoWork(); 
}

あなたは得るだろうCS0117: 'object' does not contain a definition for 'DoWork'

アップデート

C# 設計チームは、これについて既に考えています。これは、ランタイム サポートなしでは効率的に実装できず、2019 年 5 月に削除されました。ランタイムの最適化により、DIM 呼び出しが他の呼び出しと同じくらい安価になり、ボクシングなどを行う必要がなくなります。

提案された構文base(IMyInterface)call です:

interface I1
{ 
    void M(int) { }
}

interface I2
{
    void M(short) { }
}

interface I3
{
    override void I1.M(int) { }
}

interface I4 : I3
{
    void M2()
    {
        base(I3).M(0) // What does this do?
    }
}
于 2019-12-18T08:13:29.787 に答える