新規とオーバーライドの違い


198

次の違いは何ですか?

ケース1:基本クラス

public void DoIt();

ケース1:継承されたクラス

public new void DoIt();

ケース2:基本クラス

public virtual void DoIt();

ケース2:継承されたクラス

public override void DoIt();

ケース1と2はどちらも、私が実行したテストに基づいて同じ効果があるようです。違いはありますか、それとも望ましい方法ですか?


回答:


267

オーバーライド修飾子は仮想メソッドで使用でき、抽象メソッドで使用する必要があります。これは、コンパイラーが最後に定義されたメソッドの実装を使用することを示します。メソッドが基本クラスへの参照で呼び出された場合でも、それをオーバーライドする実装を使用します。

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

Derived.DoItオーバーライドする場合に呼び出されますBase.DoIt

新しい修飾子は、親クラスの実装の代わりに子クラスの実装を使用するようにコンパイラーに指示します。クラスを参照していないが親クラスを参照するコードは、親クラスの実装を使用します。

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

最初に電話しBase.DoIt、次に電話しますDerived.DoIt。これらは、派生メソッドが基本メソッドをオーバーライドするのではなく、事実上同じ名前を持つ2つの完全に別個のメソッドです。

出典:Microsoftブログ


5
This indicates for the compiler to use the last defined implementation of a method。メソッドの最後に定義された実装をどのように見つけることができますか?
AminM 2014年

5
具体的なクラスから始めて、対象のメソッドの実装があるかどうかを確認します。もしそうなら、あなたは終わりです。そうでない場合は、継承階層を1つ上に移動します。つまり、スーパークラスに目的のメソッドがあるかどうかを確認します。目的のメソッドが見つかるまで続けます。
csoltenborn 2014年

2
またoverride、基本クラスでメソッドがとして定義されている場合にのみメソッドを使用できることに注意してくださいvirtual。単語はvirtual、基本クラスIは、このメソッドを呼び出したとき、私は本当に私が実際に実行時に呼んでいるものをメソッドの実装事前に知っていないので、ちょっと、それは事実上、派生実装に置き換えられている可能性」と言っている。だから、のvirtualが意味あります。メソッドのプレースホルダは、これはとしてマークされない方法を意味virtualオーバーライドすることはできませんがしかし、あなたがすることができます。置き換える修飾子を派生クラスで任意の非仮想メソッドをnew派生レベルでのみアクセスでき、。
エリックBongers

177

virtual:メソッドが継承者によってオーバーライドされる可能性があることを示します

override:基本クラスの仮想メソッドの機能をオーバーライドして、さまざまな機能を提供します。

new:元のメソッド(仮想である必要はありません)を非表示にし、さまざまな機能を提供します。これは絶対に必要な場合にのみ使用してください。

メソッドを非表示にしても、基本クラスにアップキャストすることで元のメソッドにアクセスできます。これは一部のシナリオでは便利ですが、危険です。


2
基本メソッドを非表示にするメソッドをキャストすることが危険なのはなぜですか?それとも、一般的にアップキャストが危険であることを示唆していますか?
マーク

3
@Mark-呼び出し元は実装を認識していないため、誤って誤用する可能性があります。
ジョンB

あなたが使用することができoverride、および/またはnewなしでvirtual、親法上の?
アーロンフランケ

16

最初のケースでは、親クラスの定義を隠しています。これは、オブジェクトを子クラスとして処理しているときにのみ呼び出されることを意味します。クラスを親の型にキャストすると、親のメソッドが呼び出されます。2番目のインスタンスでは、メソッドがオーバーライドされ、オブジェクトが子クラスまたは親クラスとしてキャストされているかどうかに関係なく呼び出されます。


7

以下を試してください:(case1)

((BaseClass)(new InheritedClass())).DoIt()

編集:virtual + overrideは実行時に解決されます(したがって、overrideは実際に仮想メソッドをオーバーライドします)。一方、新しいものは同じ名前で新しいメソッドを作成し、古いものを非表示にしますが、コンパイル時に解決されます->コンパイラーがメソッドを呼び出します '見る


3

ケース1で、型が基本クラスとして宣言されているときに継承クラスのDoIt()メソッドを呼び出して使用した場合、基本クラスのアクションも表示されます。

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

受け取った警告またはエラーを投稿してください。私が最初に投稿したとき、このコードは問題なく動作しました。
マシューホワイト

これはすべてエントリポイントクラス(プログラム)内に貼り付ける必要があります。このサイトでより良いフォーマットを可能にするために削除されました。
マシューホワイト

3

2つのケースの違いは、ケース1では、ベースDoItメソッドがオーバーライドされず、単に隠されるだけです。これは、変数のタイプに応じて、呼び出されるメソッドに依存することを意味しています。例えば:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

これは本当に混乱する可能性があり、予期しない動作を引き起こす可能性があるため、可能であれば回避する必要があります。したがって、推奨される方法はケース2です。


3
  • newは、REFERENCEタイプ(の左側)を尊重して=、参照タイプのメソッドを実行することを意味します。再定義されたメソッドにnewキーワードがない場合は、そのように動作します。さらに、それは非多相継承としても知られています。つまり、「基本クラスの同じ名前のメソッドとはまったく関係のない、まったく新しいメソッドを派生クラスで作成しています。」-言ったウィテカー
  • overridevirtualは、基本クラスでキーワードとともに使用する必要があるため、OBJECTタイプ(の右側)を尊重し、=参照タイプに関係なくオーバーライドされたメソッドを実行することを意味します。さらに、それは多相継承としても知られています。

両方のキーワードが互いに反対であることを覚えておくための私の方法。

overridevirtualメソッドをオーバーライドするには、キーワードを定義する必要があります。overrideキーワードを使用するメソッド。参照タイプ(基本クラスまたは派生クラスの参照)に関係なく、基本クラスでインスタンス化された場合、基本クラスのメソッドが実行されます。それ以外の場合は、派生クラスのメソッドが実行されます。

new:キーワードがメソッドによって使用される場合、キーワードとは異なりoverride、参照タイプは重要です。派生クラスでインスタンス化され、参照型が基本クラスの場合、基本クラスのメソッドが実行されます。派生クラスでインスタンス化され、参照型が派生クラスである場合、派生クラスのメソッドが実行されます。つまり、overrideキーワードの対比です。ちなみに、メソッドに新しいキーワードを追加するのを忘れたり省略したりすると、newキーワードが使用されるため、コンパイラはデフォルトで動作します。

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

メインで呼び出す:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

出力:

A
B
B
base test
derived test

新しいコード例、

1つずつコメントして、コードを操作します。

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

キーワードoverrideが派生クラスで使用されている場合、そのキーワードは親メソッドをオーバーライドします。

キーワードnewが派生クラスで使用されている場合、派生メソッドは親メソッドによって非表示にされています。


1

私は同じ質問をしましたが、それは本当に混乱します。基本クラスのオブジェクトと派生クラスの値でのみ機能するオーバーライド新しいキーワードを検討する必要があります。この場合、あなただけがオーバーライドして、新しいの効果を見ることができます:あなたが持っているので、場合class ABBから継承しA、その後、あなたは、このようなオブジェクトをインスタンス化します。

A a = new B();

次に、メソッドを呼び出すときにその状態を考慮します。 オーバーライド:メソッドの機能を拡張し、派生クラスのメソッドを使用するのに対し、newはコンパイラに派生クラスのメソッドを非表示にし、代わりに基本クラスのメソッドを使用するように指示します。ここにその主題への非常に良い光景があります:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

以下の記事はvb.netにありますが、新しいvsオーバーライドに関する説明は非常に理解しやすいと思います。

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

記事のある時点で、次の文があります。

一般に、Shadowsは型に関連付けられた関数が呼び出されることを想定し、Overridesはオブジェクト実装が実行されることを想定しています。

この質問に対する受け入れられた回答は完璧ですが、この記事は、これら2つのキーワードの違いについてより良い意味を追加するための良い例を提供していると思います。


1

これらすべての中で、新しいものが最も混乱します。実験を通じて、新しいキーワードは、型を明示的に定義することによって、継承クラスの実装を基本クラスの実装でオーバーライドするオプションを開発者に与えるようなものです。それは逆に考えるようなものです。

以下の例では、タイプがBaseClassテストとして明示的に定義されるまで、結果は「派生結果」を返します。その後、「基本結果」のみが返されます。

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
反対する場合はコメントを追加してください。ヒットアンドランはとても臆病です。
有用なビー

0

これらのテストでは、機能の違いは示されません。

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

この例では、呼び出されるDoitは、呼び出されると予想されるものです。

違いを確認するには、次のようにする必要があります。

BaseClass obj = new DerivedClass();

obj.DoIt();

ケース1(定義したとおり)でDoIt()in BaseClassが呼び出され、ケース2(定義したとおり)でDoIt()in DerivedClassが呼び出されるテストを実行するかどうかがわかります。


-1

最初のケースでは、新しいキーワードが基本クラスのDoIt()メソッドを隠すため、派生クラスのDoIt()メソッドを呼び出します。

2番目のケースでは、オーバーライドされたDoIt()を呼び出します

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

これらのクラスのインスタンスを作成しましょう

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

上ですべてが期待されています。instanceBとinstanceCをinstanceAに設定し、DoIt()メソッドを呼び出して結果を確認します。

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt(); B ::
DoIt

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.