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


90

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

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


8
オーバーロードまたはオーバーライドを意味しますか?オーバーロード=同じ名前でパラメーターが異なるメソッドを追加します(たとえば、Console.WriteLineの異なるオーバーロード)。オーバーライド=(おおまかに)メソッドのデフォルトの動作を変更します(たとえば、Circle、Rectangleなどの動作が異なるShape.Drawメソッド)。派生クラスのメソッドはいつでもオーバーロードできますが、オーバーライドは仮想メソッドにのみ適用されます。
itowlson 2009

回答:


111

いいえ、非仮想メソッドをオーバーライドすることはできません。最も近い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メソッドがprintを呼び出していますFoo.M。あなたがこのアプローチを見ることができるように、そのオブジェクトへの参照が正しい派生型であるが、基本メソッドを非表示にして、あなたがいる限りメソッドの新しい実装を持つことができないブレーク多型を。

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

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

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

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

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


6
「実行時間ポリモーフィックディスパッチ」は技術的には正しい言い方ですが、おそらくこれはほぼ全員の頭を超えていると思います。
オリオンエドワーズ、

3
作者がメソッドのオーバーライドを許可しないことを意図していたのは事実ですが、それが必ずしも正しいことであるとは限りません。XNAチームはIGraphicsDeviceインターフェイスを実装して、ユーザーがデバッグとユニットテストをより柔軟に行えるようにすべきだったと思います。私はいくつかの非常に醜いことをすることを余儀なくされており、これはチームによって予見されたはずです。詳細については、forums.xna.com
forums

2
@Orion私もそれを理解していませんでしたが、簡単なグーグルの後で、「正しい」用語の使用を見て感謝しました。
zfedoran 2009

9
「作者はそれを意図していなかった」という議論はまったくしません。それは、ホイールの発明者がホイールが車に装着されることを予期していなかったため、車で使用できないと言っているようなものです。ホイール/メソッドがどのように機能するかを知っているので、少し違うことをしたいと思っています。クリエイターが私に望んでもそうでなくても構いません。死んだスレッドを復活させようとして申し訳ありません。私がいるこの問題を回避するためのいくつかの本当に不便な方法を理解する前に、私はいくつかの蒸気を吹き飛ばす必要があります:)
Jeff

2
では、大規模なコードベースを継承し、新しい機能のテストを追加する必要があるが、それをテストするには、多くの機械をインスタンス化する必要がある場合はどうでしょうか。Javaでは、これらの部分を拡張し、スタブで必要なメソッドをオーバーライドするだけです。C#では、メソッドが仮想としてマークされていない場合は、他のメカニズム(モックライブラリなど)を見つける必要があります。
Adam Parkin 2014

22

いいえ、できません。

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

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

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

6

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


3

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

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

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


2

(CLRをハックせずに)C#のクラスの非仮想メソッドをオーバーライドすることはできませんが、クラスが実装するインターフェイスのメソッドはオーバーライドできます。封印されていない

class GraphicsDevice: IGraphicsDevice {
    public void DoWork() {
        Console.WriteLine("GraphicsDevice.DoWork()");
    }
}

// with its interface
interface IGraphicsDevice {
    void DoWork();
}

// You can't just override DoWork in a child class,
// but if you replace usage of GraphicsDevice to IGraphicsDevice,
// then you can override this method (and, actually, the whole interface).

class MyDevice: GraphicsDevice, IGraphicsDevice {
    public new void DoWork() {
        Console.WriteLine("MyDevice.DoWork()");
        base.DoWork();
    }
}

そして、ここにデモがあります

class Program {
    static void Main(string[] args) {

        IGraphicsDevice real = new GraphicsDevice();
        var myObj = new MyDevice();

        // demo that interface override works
        GraphicsDevice myCastedToBase = myObj;
        IGraphicsDevice my = myCastedToBase;

        // obvious
        Console.WriteLine("Using real GraphicsDevice:");
        real.DoWork();

        // override
        Console.WriteLine("Using overriden GraphicsDevice:");
        my.DoWork();

    }
}

@ダンはライブデモです:dotnetfiddle.net/VgRwKK
Vyacheslav Napadovsky

0

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


0

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

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

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

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

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

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

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());

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


-5

抽象クラスと抽象メソッドを使用してこれを実現する方法があります。

検討する

Class Base
{
     void MethodToBeTested()
     {
        ...
     }

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

メソッドMethodToBeTested()の異なるバージョンが必要な場合は、クラスベースを抽象クラスに変更し、メソッドMethodToBeTested()を抽象メソッドとして変更します。

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

抽象void MethodToBeTested()には問題があります。実装はなくなりました。

したがってclass DefaultBaseImplementation : Base、デフォルトの実装を持つを作成します。

class UnitTestImplementation : Baseユニットテストを実装する別のものを作成します。

これらの2つの新しいクラスを使用すると、基本クラスの機能をオーバーライドできます。

Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

これで、2つのクラスが実装(オーバーライド)されましたMethodToBeTested()

必要に応じて(派生)クラスをインスタンス化できます(つまり、基本実装または単体テスト実装のいずれかを使用)。


@slavoo:こんにちはslavoo。コードを更新していただきありがとうございます。しかし、これを反対する理由を教えていただけますか?
ShivanandSK 2015

6
それは質問に答えないからです。彼は、仮想とマークされていないメンバーを上書きできるかどうかを尋ねています。アブストラクトとマークされたメンバーを実装する必要があることを示しました。
Lee Louviere、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.