子クラスに実装するときに、抽象メソッドの前にオーバーライドキーワードを指定する必要があるのはなぜですか?


9

抽象クラスから継承するクラスを作成し、継承した抽象クラスを実装するときに、overrideキーワードを使用する必要があるのはなぜですか。

public abstract class Person
{
    public Person()
    {

    }

    protected virtual void Greet()
    {
        // code
    }

    protected abstract void SayHello();
}

public class Employee : Person
{
    protected override void SayHello() // Why is override keyword necessary here?
    {
        throw new NotImplementedException();
    }

    protected override void Greet()
    {
        base.Greet();
    }
}

メソッドは親クラスで抽象として宣言されているため、親クラスには実装がないため、ここでキーワードのオーバーライドが必要なのはなぜですか?


2
抽象メソッドは、仕様
Pavel Anikhouski


「オーバーライド修飾子は、継承されたメソッド、プロパティ、インデクサー、またはイベントの抽象実装または仮想実装を拡張または変更するために必要です。」docs.microsoft.com/en-us/dotnet/csharp/language-reference/...
gunr2171

2
そこに配置しないと、メソッドをオーバーライドする代わりに「非表示」にするからです。あなたがいることを警告取得だからこその「それはあなたが意図したものであれば、使用しnewていないメソッドのオーバーライド、基本クラスのメソッド『エラーkeyword`は...あなたも取得したい』。
ロン・ベイヤーに

仮想メソッドを使用した@RonBeyerはありますが、抽象を使用するとコンパイルできません。
Johnathan Barclay

回答:


14

抽象クラスから継承するクラスを作成し、継承した抽象クラスを実装するときに、overrideキーワードを使用する必要があるのはなぜですか。

"なぜ?" このような質問はあいまいなので、答えるのが難しい場合があります。私はあなたの質問が「overrideキーワードが必要であるという立場を主張するために言語設計の間にどんな議論がなされ得るか」であると仮定します。

まず、一歩下がってみましょう。Javaなどの一部の言語では、メソッドはデフォルトで仮想であり、自動的にオーバーライドされます。C#の設計者はこれを認識しており、Javaのマイナーな欠陥であると考えていました。C#は「バカなパーツが取り除かれたJava」ではありませんが、C#の設計者はC、C ++、Javaの問題のある設計ポイントから学び、C#でそれらを複製しないことに熱心でした。

C#の設計者は、オーバーライドがバグの原因である可能性があると考えました。結局のところ、これは既存のテスト済みコードの動作変更する方法であり、危険です。オーバーライドは、何気なくまたは偶然に行われるべきものではありません。それはそれについて熱心に考えている誰かによって設計されるべきです。そのため、デフォルトではメソッドは仮想ではなく、メソッドをオーバーライドしていると言う必要があります。

それが基本的な推論です。ここで、さらに高度な推論に進むことができます。

StriplingWarriorの答えは、より高度な議論をする上での最初の切り口となります。派生クラスの作成者は、基本クラスについて知らされていない可能性があり、新しいメソッドを作成しようとしている可能性があり、ユーザーが誤ってオーバーライドできないようにする必要があります

この点は理にかなっていますが、次のような多くの反論があります。

  • 派生クラスの作成者は、基本クラスに関するすべてを知る責任があります。彼らはそのコードを再利用しており、再利用する前にそのコードを完全に理解するために十分な注意を払う必要があります。
  • 特定のシナリオでは、仮想メソッドは抽象的です。オーバーライドしないとエラーになるので、作成者が誤って実装を作成することはまずありません。

次に、この点についてさらに高度な議論をしましょう。派生クラスの作成者は、どのような状況で基本クラスが何をしているのかわからないことを許されますか? さて、このシナリオを検討してください:

  • 基本クラスの作成者は、抽象基本クラスBを作成します。
  • 別のチームの派生クラスの作成者は、メソッドMを使用して派生クラスDを作成します。
  • 基本クラスの作成者は、基本クラスBを拡張するチームが常にメソッドMを提供する必要があることを理解しているため、基本クラスの作成者は抽象メソッドMを追加します。
  • クラスDを再コンパイルするとどうなりますか?

私たちが起こしたいのは、Dの作者に関連する何かが変更されたという知らせがあることです。変更された関連事項は、Mが要件になり、実装をオーバーロードする必要があることです。 DMは、基本クラスから呼び出すことができることがわかったら、その動作を変更する必要があるかもしれません。 正しいことは、「ああ、DMは存在し、BMを拡張する」と黙って言うのではありません。コンパイラが行う正しいことはfailであり、「Dの作者、もう有効ではないあなたの仮定を確認し、必要に応じてコードを修正してください」と言います。

あなたの例では、抽象メソッドをオーバーライドしているため、overrideオプションであったと仮定しSayHelloます。2つの可能性があります:(1)コードの作成者が抽象メソッドをオーバーライドしようとしている、または(2)誰かがベースクラスを変更したためにオーバーライドメソッドが誤ってオーバーライドされ、コードが微妙に間違っている。 オプションである場合、これらの可能性を区別することはできませんoverride

しかし、必要な場合overrideは、3つのシナリオを区別できます。コードで可能な間違いがある場合は、されて行方不明。意図的に上書きする場合は存在します。そしてそれが意図的に上書きされていない場合は存在します。C#の設計により、これらの微妙な区別を行うことができます。 overrideoverridenew

コンパイラエラーレポートを作成するには、開発者の心を読む必要があることに注意してください。コンパイラは、作成者が考えていた正しいコードを間違ったコードから推定し、それらを正しい方向に向けるエラーを提供する必要があります。開発者が考えていたことについてコードに残せる手がかりが多いほど、コンパイラーがエラーを報告する際の作業が改善されるため、バグをより早く見つけて修正することができます。

しかし、より一般的には、C#はコードが変更される世界向けに設計されました。「奇数」のように見えるC#の非常に多くの機能が実際にあります。これは、基本クラスが変更されたために以前は有効であったという仮定が無効になったときに開発者に通知するためです。このクラスのバグは「脆弱な基本クラスの障害」と呼ばれ、C#には、この障害クラスに対する多くの興味深い緩和策があります。


4
詳しく説明していただきありがとうございます。私は常にあなたの答えに感謝します。なぜなら、「内側」の誰かからの権威ある声を持つことは良いことであり、物事を簡単かつ完全に説明する素晴らしい仕事をするからです。
StriplingWarrior

詳しい説明ありがとうございます!心から感謝する。
psj01

素晴らしいポイント!言及している他の緩和策をいくつか挙げていただけますか?
aksh1618

3

親クラスの別のメソッドをオーバーライドするか、クラス階層のこのレベルに固有の新しい実装を作成するかを指定します。プログラマーが、クラスで作成したものとまったく同じシグニチャーを持つ親クラスのメソッドの存在に気付かない可能性があると考えられます。

抽象メソッドを非抽象子クラスでオーバーライドする必要があることは事実ですが、C#の作成者はおそらく、何をしようとしているのかを明示するほうがよいと感じました。


これは、言語設計チームの推論を理解する上での良いスタートです。ここであなたが表現したアイデアからチームがどのように始まるかを示す回答を追加しましたが、さらに一歩進んでいます。
Eric Lippert

1

そのためabstractの方法は、C#言語ごとに、無実装と仮想メソッドである仕様、抽象メソッドが暗黙的に仮想メソッドであることを意味しています。そして、overrideあなたが見ることができるように、抽象的または仮想実装を拡張または変更するために使用され、ここで

少し言い換えると、仮想メソッドを使用してある種の遅延バインディングを実装しますが、抽象メソッドは型のサブクラスにメソッドを明示的にオーバーライドさせる必要があります。これがポイントです。メソッドがの場合virtual、オーバーライドできます。それがの場合、オーバーライドabstractする必要があります


1
問題はそれが何をするのかではなく、なぜそれが明示的でなければならないのかと思います。
Johnathan Barclay

0

@StriplingWarriorの答えに追加するために、基本クラスの仮想メソッドのオーバーライドと一貫性のある構文を使用することも行われたと思います。

public abstract class MyBase
{
    public virtual void MyVirtualMethod() { }

    public virtual void MyOtherVirtualMethod() { }

    public abstract void MyAbtractMethod();
}

public class MyDerived : MyBase
{
    // When overriding a virtual method in MyBase, we use the override keyword.
    public override void MyVirtualMethod() { }

    // If we want to hide the virtual method in MyBase, we use the new keyword.
    public new void MyOtherVirtualMethod() { }

    // Because MyAbtractMethod is abstract in MyBase, we have to override it: 
    // we can't hide it with new.
    // For consistency with overriding a virtual method, we also use the override keyword.
    public override void MyAbtractMethod() { }
}

したがって、抽象メソッドをオーバーライドするためのオーバーライドキーワードを必要としないようにC#を設計できたかもしれませんが、仮想メソッドのオーバーライドと整合しないため、設計者は混乱を招くと判断したと思います。


再:「仮想メソッドのオーバーライドと一致しないため、設計者は混乱を招くと判断しました」-はい、しかしそれ以上です。仮想メソッドMを持つ基本クラスBと、オーバーライドを持つ派生クラスDがあるとします。ここで、Bの作成者がMを抽象化することを決定したとします。それは重大な変更ですが、おそらくそうするでしょう。質問:Dの作成者は、override ほとんどの人は、Dの作者に不必要なコード変更を強制するのはばかげていることに同意するでしょう。彼らのクラスは大丈夫です!
Eric Lippert
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.