非仮想メソッドをオーバーライドする方法はありますか?または同様の結果をもたらすもの(目的のメソッドを呼び出す新しいメソッドを作成する以外)?
Microsoft.Xna.Framework.Graphics.GraphicsDevice
単体テストを念頭に置いてからメソッドをオーバーライドしたいと思います。
非仮想メソッドをオーバーライドする方法はありますか?または同様の結果をもたらすもの(目的のメソッドを呼び出す新しいメソッドを作成する以外)?
Microsoft.Xna.Framework.Graphics.GraphicsDevice
単体テストを念頭に置いてからメソッドをオーバーライドしたいと思います。
回答:
いいえ、非仮想メソッドをオーバーライドすることはできません。最も近い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
ます。
オーバーロードとオーバーライドが混乱していると思いますが、オーバーロードとは、名前が同じでパラメータセットが異なる2つ以上のメソッドがあることを意味しますが、オーバーライドは、派生クラスのメソッドの実装が異なることを意味します。それは基本クラスです)。
メソッドが仮想の場合、派生クラスのoverrideキーワードを使用してメソッドをオーバーライドできます。ただし、非仮想メソッドは、overrideキーワードの代わりにnewキーワードを使用することによってのみ、基本実装を非表示にできます。コンパイラが基本メソッドへの静的ディスパッチを使用するため、呼び出し元が基本タイプとして型指定された変数を介してメソッドにアクセスする場合、非仮想ルートは役に立ちません(派生クラスのコードが呼び出されることは決してない)。
既存のクラスにオーバーロードを追加することを妨げるものは決してありませんが、クラスについて知っているコードだけがそれにアクセスできます。
(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();
}
}
非派生クラスから継承する場合は、抽象スーパークラスを作成し、代わりにそれからダウンストリームから継承できます。
非仮想メソッドをオーバーライドする方法はありますか?または同様の結果をもたらすもの(目的のメソッドを呼び出す新しいメソッドを作成する以外)?
非仮想メソッドをオーバーライドすることはできません。ただし、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());
また、アクセス修飾子も同じであることを確認する必要があります。そうでない場合、継承を継承しません。別のクラスClass1
がnew
キーワード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());
前の回答で指摘したように、これは良い設計原則ではありません。
抽象クラスと抽象メソッドを使用してこれを実現する方法があります。
検討する
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()
。
必要に応じて(派生)クラスをインスタンス化できます(つまり、基本実装または単体テスト実装のいずれかを使用)。