web-dev-qa-db-ja.com

非仮想メソッドをオーバーライドすることは可能ですか?

非仮想メソッドをオーバーライドする方法はありますか?または同様の結果を与えるもの(新しいメソッドを作成して目的のメソッドを呼び出す以外)?

単体テストを念頭に置いてMicrosoft.Xna.Framework.Graphics.GraphicsDeviceからのメソッドをオーバーライドしたいと思います。

82
zfedoran

いいえ、非仮想メソッドをオーバーライドすることはできません。最も近い方法は、同じ名前のnewメソッドを作成してメソッドを非表示にすることですが、これは優れた設計原則に反するためお勧めできません。

ただし、メソッドを非表示にしても、実際の仮想メソッド呼び出しのように、メソッド呼び出しの実行時間をポリモーフィックにディスパッチすることはできません。この例を考えてみましょう:

using System;

class Example
{
    static void Main()
    {
        Foo f = new Foo();
        f.M();

        Foo b = new Bar();
        b.M();
    }
}

class Foo
{
    public void M()
    {
        Console.WriteLine("Foo.M");
    }
}

class Bar : Foo
{
    public new void M()
    {
        Console.WriteLine("Bar.M");
    }
}

この例では、両方のMメソッドの呼び出しがFoo.Mを出力します。ご覧のとおり、このアプローチを使用すると、そのオブジェクトへの参照が正しい派生型であるが、基本メソッドを非表示にしている限り、メソッドの新しい実装を使用できますdoesポリモーフィズムを破壊します。

この方法で基本メソッドを非表示にしないことをお勧めします。

私は、メソッドがデフォルトでは(Javaではなく)非仮想であるというC#のデフォルトの振る舞いを好む人を支持する傾向があります。さらに進んで、クラスもデフォルトで封印する必要があると言います。継承を適切に設計することは困難であり、仮想としてマークされていないメソッドがあるという事実は、そのメソッドの作成者がメソッドのオーバーライドを意図していないことを示しています。

編集:「実行時間ポリモーフィックディスパッチ」

これが意味することは、仮想メソッドを呼び出すときに実行時に発生するデフォルトの動作です。たとえば、以前のコード例では、非仮想メソッドを定義するのではなく、実際には仮想メソッドと真のオーバーライドメソッドも定義したとしましょう。

その場合にb.Fooを呼び出すと、CLRはb参照が指すオブジェクトのタイプをBarとして正しく決定し、Mの呼び出しをディスパッチします。適切に。

102
Andrew Hare

いいえ、できません。

仮想メソッドのみオーバーライドできます- MSDN here を参照してください:

C#では、派生クラスに基本クラスメソッドと同じ名前のメソッドを含めることができます。

  • 基本クラスメソッドは仮想で定義する必要があります。
21
ChrisF

基本クラスがシールされていない場合は、基本クラスを継承して、基本クラスを非表示にする新しいメソッドを作成できます(メソッド宣言で「new」キーワードを使用します)。それ以外の場合は、元の作成者がオーバーライドすることを意図していないため、オーバーライドできないため、仮想ではない理由はありません。

5
slugster

オーバーロードと混乱をオーバーライドしていると思います、オーバーロードとは、同じ名前でパラメーターのセットが異なる2つ以上のメソッドがあることを意味します基本クラスです)。

メソッドが仮想の場合、派生クラスのoverrideキーワードを使用してメソッドをオーバーライドできます。ただし、非仮想メソッドは、オーバーライドキーワードの代わりに新しいキーワードを使用することによってのみ基本実装を非表示にできます。コンパイラが基本メソッドへの静的ディスパッチを使用するため、呼び出し側が基本型として型指定された変数を介してメソッドにアクセスする場合、非仮想ルートは役に立ちません(派生クラスのコードが呼び出されないことを意味します)。

既存のクラスにオーバーロードを追加することを妨げるものはありませんが、クラスについて知っているコードのみがアクセスできます。

2
Rory

非派生クラスから継承する場合、単純に抽象スーパークラスを作成し、代わりにダウンストリームから継承することができます。

0
user3853059

非仮想メソッドをオーバーライドする方法はありますか?または同様の結果を与えるもの(新しいメソッドを作成して目的のメソッドを呼び出す以外)?

非仮想メソッドをオーバーライドすることはできません。ただし、cannew修飾子キーワードを使用して同様の結果を得ることができます。

class Class0
{
    public int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());

また、 access modifier も同じであることを確認する必要があります。そうしないと、継承されません。別のクラスがClass1を継承する場合、Class1newキーワードは、アクセス修飾子が同じでない限り、そのクラスを継承するオブジェクトに影響しません。

アクセス修飾子が同じでない場合:

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // different access modifier
    new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 0
Console.WriteLine(new Class2().Result());

...アクセス修飾子と同じ場合:

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // same access modifier
    protected new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 1
Console.WriteLine(new Class2().Result());

前の回答で指摘したように、これは良い設計原則ではありません。

0
Aaron Thomas