virtual
子型で一致するメソッドを宣言するときoverride
に単純に「new
11 に答える
このようなことは、写真を使った方が理解しやすいといつも思っています。
繰り返しますが、ジョセフ・デイグルのコードを使用すると、
public class Foo
{
public /*virtual*/ bool DoSomething() { return false; }
}
public class Bar : Foo
{
public /*override or new*/ bool DoSomething() { return true; }
}
次に、次のようなコードを呼び出す場合:
Foo a = new Bar();
a.DoSomething();
注: 重要なことは、オブジェクトは実際にはですBar
が、型の変数に格納していることですFoo
(これはキャストに似ています)。
クラスを宣言するときにvirtual
/を使用したoverride
かどうかに応じて、結果は次のようになります。new
「new」キーワードはオーバーライドされません。基本クラスのメソッドとは関係のない新しいメソッドを意味します。
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
public class Test
{
public static void Main ()
{
Foo test = new Bar ();
Console.WriteLine (test.DoSomething ());
}
}
これは false を出力します。override を使用すると、true が出力されます。
(基本コードは Joseph Daigle から取得)
したがって、実際のポリモーフィズムを行っている場合は、常に OVERRIDEを使用する必要があります。「new」を使用する必要がある唯一の場所は、メソッドが基本クラスのバージョンにまったく関係がない場合です。
仮想メソッドと非仮想メソッドの動作の違いを理解するためのコードを次に示します。
class A
{
public void foo()
{
Console.WriteLine("A::foo()");
}
public virtual void bar()
{
Console.WriteLine("A::bar()");
}
}
class B : A
{
public new void foo()
{
Console.WriteLine("B::foo()");
}
public override void bar()
{
Console.WriteLine("B::bar()");
}
}
class Program
{
static int Main(string[] args)
{
B b = new B();
A a = b;
a.foo(); // Prints A::foo
b.foo(); // Prints B::foo
a.bar(); // Prints B::bar
b.bar(); // Prints B::bar
return 0;
}
}
このnew
キーワードは、実際には、その特定の型にのみ存在する完全に新しいメンバーを作成します。
例えば
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
メソッドは両方のタイプに存在します。リフレクションを使用して type のメンバーを取得すると、まったく同じように見えるBar
2 つのメソッドが実際に呼び出されていることがわかります。DoSomething()
を使用new
することで、基本クラスの実装を効果的に非表示にできるため、(私の例では) からクラスが派生するときに、Bar
へのメソッド呼び出しが にbase.DoSomething()
移動し、 に移動Bar
しませんFoo
。
技術的な詳細だけでなく、仮想/オーバーライドを使用すると、設計に関する多くのセマンティック情報が伝達されると思います。メソッドvirtualを宣言するときは、実装クラスが独自のデフォルト以外の実装を提供することを期待していることを示します。同様に、基本クラスでこれを省略すると、すべての実装クラスに対してデフォルトのメソッドで十分であるという期待が宣言されます。同様に、抽象宣言を使用して、実装クラスに独自の実装を提供させることができます。繰り返しになりますが、これは、プログラマーがコードの使用をどのように期待しているかについて多くのことを伝えていると思います。基本クラスと実装クラスの両方を作成していて、newを使用していることに気付いた場合は、親でメソッドを仮想化しないという決定を真剣に考え直し、自分の意図を具体的に宣言します。
virtual / overrideは、2つのメソッドが関連していること、および状況によっては最初の(仮想)メソッドを呼び出していると思われる場合に、代わりに2番目の(オーバーライドされた)メソッドを呼び出すことが実際に正しいことをコンパイラーに通知します。これがポリモーフィズムの基盤です。
(new SubClass() as BaseClass).VirtualFoo()
サブクラスのオーバーライドされたVirtualFoo()メソッドを呼び出します。
newは、基本クラスのメソッドと同じ名前の派生クラスにメソッドを追加しているが、それらは相互に関係がないことをコンパイラーに通知します。
(new SubClass() as BaseClass).NewBar()
BaseClassのNewBar()メソッドを呼び出しますが、次のようになります。
(new SubClass()).NewBar()
サブクラスのNewBar()メソッドを呼び出します。
new
キーワードは非表示用です。- 実行時にメソッドを隠していることを意味します。出力は基本クラスのメソッドに基づいています。override
オーバーライドするため。- 基本クラスの参照を使用して派生クラス メソッドを呼び出していることを意味します。出力は、派生クラス メソッドに基づきます。
私のバージョンの説明は、プロパティを使用して違いを理解するのに役立ちます。
override
簡単ですよね?基になる型は親の型をオーバーライドします。
new
おそらく誤解を招く可能性があります(私にとってはそうでした)。プロパティを使用すると、理解しやすくなります。
public class Foo
{
public bool GetSomething => false;
}
public class Bar : Foo
{
public new bool GetSomething => true;
}
public static void Main(string[] args)
{
Foo foo = new Bar();
Console.WriteLine(foo.GetSomething);
Bar bar = new Bar();
Console.WriteLine(bar.GetSomething);
}
デバッガーを使用すると、実際にはとの 2 つのバージョンのプロパティがあるため、 には 2 つのプロパティがFoo foo
あること がわかります。どちらを使用するかを知るために、c# は現在の型のプロパティを "選択" します。GetSomething
Foo
Bar
Bar のバージョンを使用したい場合は、Foo foo
代わりに override または use を使用していたでしょう。
Bar bar
のまったく新しい動作が必要なため、 は1 つしかありません。GetSomething
メソッドを何もマークしないということは、実行時型ではなく、オブジェクトのコンパイル型を使用してこのメソッドをバインドすることです (静的バインディング)。
メソッドを手段でマークvirtual
する: コンパイル時の型ではなく、オブジェクトの実行時の型を使用してこのメソッドをバインドします (動的バインディング)。
virtual
基本クラスメソッドをin 派生クラスでマークするoverride
ことは、次のことを意味します。これは、オブジェクトのランタイム タイプを使用してバインドされるメソッドです (動的バインディング)。
virtual
基本クラスメソッドをin 派生クラスでマークするnew
ことは、次のことを意味します。これは新しいメソッドであり、基本クラスの同じ名前のメソッドとは関係がなく、オブジェクトのコンパイル時の型 (静的バインディング) を使用してバインドする必要があります。
virtual
派生クラスで基本クラス メソッドをマークしないということは、次のことを意味します。このメソッドはnew
(静的バインディング) としてマークされます。
メソッドをマークするということは、次のことabstract
を意味します。このメソッドは仮想ですが、本体は宣言せず、そのクラスも抽象 (動的バインディング) です。