62

C# でのメソッドのオーバーライドと非表示について少し混乱しています。それぞれの実用的な使用法と、それぞれをいつ使用するかについての説明も歓迎します。

オーバーライドについて混乱しています - なぜオーバーライドするのですか? これまでに学んだことは、オーバーリングによって、シグネチャを変更せずに、派生クラスのメソッドに必要な実装を提供できるということです。

スーパークラスのメソッドをオーバーライドせずにサブクラスのメソッドを変更すると、スーパークラスのメソッドが変更されますか?

私は次のことについても混乱しています - これは何を示していますか?

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?
}
4

3 に答える 3

143

検討:

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 の方法です (一部の言語ではそうせざるを得ません)。オブジェクトで仮想メソッドが呼び出されると、そのメソッドの最も派生したバージョンが呼び出されます。したがって、 で定義された機能isReallyDerivedが使用されます。BaseClassDerivedClass

非表示とは、まったく異なる方法を使用していることを意味します。WriteNum()onを呼び出すと、別のonisReallyDerivedがあることを知る方法がないため、呼び出されません。オブジェクトを として扱っている場合にのみ呼び出すことができます。WriteNum()DerivedClassDerivedClass

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

  1. 前方互換性。DerivedClassメソッドがDoStuff()あり、後でメソッドBaseClassを追加するために変更された場合DoStuff()(それらは別の人によって記述され、別のアセンブリに存在する可能性があることに注意してください)、メンバーの非表示を禁止するDerivedClassと、変更せずに突然バグが発生します。また、新しいDoStuff()onBaseClassが仮想である場合、それを自動的にDerivedClass上書きすると、既存のメソッドが呼び出されるべきではないときに呼び出される可能性があります。したがって、非表示がデフォルトであることは良いことです (new非表示にしたいことを明確にするために使用しますが、それを省略すると非表示になり、コンパイル時に警告が発せられます)。

  2. 貧乏人の共分散。作成されたのコピーであるnew を返すClone()on メソッドを考えてみましょう。これをオーバーライドすると が作成されますが、 として返されますが、これはあまり役に立ちません。私たちができることは、オーバーライドされた仮想保護を持つことです。には、これの結果を返す aがあり、すべて問題ありません。 inには、 a を返すnew でこれを隠します。onを呼び出すと、常に参照が返されます。参照は、必要に応じて値または値になります。を呼び出すと、BaseClassBaseClassDerivedClassDerivedClassBaseClassCreateClone()BaseClassClone()DerivedClassClone()DerivedClassClone()BaseClassBaseClassBaseClassDerivedClassClone()DerivedClassDerivedClassそのコンテキストで必要なものです。この原則には他にもバリエーションがありますが、それらはすべて非常にまれであることに注意してください。

2 番目のケースで注意すべき重要なことは使用者DerivedClassが. それを呼び出すことができる方法の結果は、互いに一貫性が保たれます。隠蔽のほとんどの場合、驚きをもたらすリスクがあるため、一般的に眉をひそめられます。これは、隠蔽によってしばしば引き起こされる問題を解決するという理由だけで正当化されます。Clone()DerivedClass

全体として、非表示は必要な場合もあれば、有用な場合もまれにありますが、一般的には悪いことなので、十分に注意してください。

于 2010-10-01T11:22:07.403 に答える
29

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

非表示とは、そのメソッドが基本クラスで として定義されていないvirtual場合、または新しい実装で が指定されていない場合に、子孫クラスでメソッドの新しい実装を提供する場合ですoverride

隠れることはしばしば悪いことです。まったく回避できる場合は、通常、それを行わないようにする必要があります。隠しメソッドは、基本クラス参照を使用する場合ではなく、定義した実際の型の変数で呼び出されたときにのみ使用されるため、非表示にすると予期しないことが発生する可能性があります... 一方、オーバーライドされた仮想メソッドは、子クラスの基本クラス参照を使用して呼び出された場合でも、適切なメソッド バージョンが呼び出されます。

たとえば、次のクラスを考えてみましょう。

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の実行

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


それがあなたが本当に望む振る舞いなら、; どうぞ。ただし、それが必要な場合は、コードに大きなアーキテクチャ上の問題がある可能性があることを強くお勧めします。

于 2010-10-01T11:12:02.967 に答える
2

メソッドのオーバーライドは、派生クラスの基本クラス メソッドのデフォルトの実装を単純にオーバーライドします。

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

なので

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

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

  }  
}  

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

于 2010-10-01T11:13:33.527 に答える