web-dev-qa-db-ja.com

オーバーライドとメソッドの非表示

C#でのメソッドのオーバーライドと非表示について少し混乱しています。 when oneがそれぞれを使用するのと同様に、それぞれの実用的な使用も評価されます。

オーバーライドについて混乱しています-なぜオーバーライドするのですか?これまでに学んだことは、オーバーライドすることで、署名を変更せずに、派生クラスのメソッドに望ましい実装を提供できることです。

スーパークラスのメソッドをオーバーライドせずにサブクラスのメソッドを変更した場合、スーパークラスのメソッドも変更されますか?

また、以下についても混乱しています-これは何を実証していますか?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}
54
NoviceToDotNet

考慮してください:

_public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz
_

オーバーライドは古典的なOOであり、派生クラスが基本クラスよりも特定の動作を持つことができます(一部の言語ではそうするしか選択肢がない)。仮想メソッドが呼び出されるときオブジェクトでは、メソッドの最も派生したバージョンが呼び出されるため、isReallyDerivedBaseClassとして扱っている場合でも、DerivedClassで定義された機能が使用されます。

非表示とは、まったく異なる方法があることを意味します。 isReallyDerivedWriteNum()を呼び出すと、DerivedClassで別のWriteNum()があることを知る方法がないため、呼び出されません。オブジェクトを処理しているときにのみ呼び出すことができますas a DerivedClass

ほとんどの場合、隠れることは悪いことです。一般的に、派生クラスで変更される可能性がある場合は、メソッドを仮想として使用し、派生クラスでオーバーライドする必要があります。ただし、次の2つの点が役立ちます。

  1. 前方互換性。 DerivedClassDoStuff()メソッドがあり、その後BaseClassDoStuff()メソッドを追加するように変更された場合(それらは異なる人々と異なるアセンブリに存在する)、メンバーの非表示の禁止は、それを変更せずに突然DerivedClassバギーにしたでしょう。また、BaseClassの新しいDoStuff()が仮想の場合、DerivedClassでそれを自動的にオーバーライドすると、既存のメソッドが必要なときに呼び出される可能性があります't。したがって、非表示がデフォルトであることは良いことです(newを使用して、明確に非表示にすることを明確にしますが、非表示のままにすると、コンパイル時に警告が表示されます)。

  2. 貧者の共分散。作成されたもののコピーである新しいBaseClassを返すBaseClassClone()メソッドを考えてください。 DerivedClassのオーバーライドでは、これはDerivedClassを作成しますが、それをBaseClassとして返しますが、これはあまり有用ではありません。できることは、オーバーライドされた仮想保護されたCreateClone()を持つことです。 BaseClassには、この結果を返すClone()があり、すべてが順調です-DerivedClassには、これを新しいClone()で非表示にしますDerivedClassを返します。 BaseClassClone()を呼び出すと、常にBaseClass参照が返されます。これは、必要に応じてBaseClass値またはDerivedClass値になります。 DerivedClassClone()を呼び出すと、DerivedClass値が返されます。これは、そのコンテキストで必要なものです。この原則には他にも変形がありますが、それらはすべて非常にまれであることに注意する必要があります。

2番目のケースで注意すべき重要なことは、DerivedClassを使用している人がClone()DerivedClassを返します。呼び出される可能性のある方法の結果は、互いに一貫性が保たれます。サプライズをもたらすリスクを隠すほとんどのケースは、一般的に眉をひそめている理由です。これは、隠蔽がしばしばもたらす問題をまさに解決するため、正確に正当化されます。

全体的に、非表示は時々必要であり、あまり有用ではありませんが、一般的には非表示なので、非常に注意してください。

125
Jon Hanna

オーバーライドは、そのメソッドがoverrideとして基本クラスで定義されているときに、下位クラスのメソッドの新しいvirtual実装を提供する場合です。

非表示は、そのメソッドがnotが基本クラスでvirtualとして定義されている場合、または新しい実装がoverride

非表示は非常に頻繁に悪いです。回避できる場合は、一般的にしないでください。 Hiddenメソッドは、基本クラス参照を使用している場合ではなく、定義した実際の型の変数で呼び出された場合にのみ使用されるため、非表示が予期せぬ事態を引き起こす可能性があります...子クラスの基本クラス参照を使用して呼び出された場合でも、適切なメソッドバージョンが呼び出されます。

たとえば、次のクラスを検討してください。

_public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}
_

一致する参照で、InheritedClassのインスタンスを使用して、次のように呼び出します。

_InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();
_

これにより、期待どおりの結果が返されます。両方のメソッドは、InheritedClassバージョンを実行していると言います。

InheritedClass Method1の実行
InheritedClass Method2の実行

このコードは、同じInheritedClassのインスタンスを作成しますが、BaseClass参照に保存します。

_BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();
_

通常、OOP原則では、上記の例と同じ出力を期待する必要がありますが、同じ出力は得られません。

InheritedClass Method1の実行
BaseClass Method2の実行

InheritedClassコードを記述したとき、Method2()へのすべての呼び出しで、コードに記述したコードを実行したい場合があります。通常、これはどのように機能するか-オーバーライドしたvirtualメソッドで作業していると仮定します。ただし、new/hiddenメソッドを使用しているため、代わりに使用している参照のバージョンを呼び出します。


それがあなたの振る舞いだとしたらtruly want、そこに行きます。しかし、もしそれがあなたが望むものであれば、コードに大きなアーキテクチャ上の問題があるかもしれないことを強くお勧めします。

25
Andrew Barber

メソッドのオーバーライドは、派生クラスの基本クラスメソッドの既定の実装を簡単にオーバーライドします。

メソッドの非表示:派生クラスの仮想メソッドの前に「新しい」キーワードを使用できます

として

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

class Bar:Foo  
{  
  public new virtual void foo1()  
  {   

  }  
}  

ここで、Barから派生した別のクラスBar1を作成すると、Barで定義されているfoo1をオーバーライドできます。

2
TalentTuner