web-dev-qa-db-ja.com

すべてのメソッドを仮想にマークする必要がありますか?

Javaでは、メソッドをfinalとしてマークしてimpossibleにオーバーライドできます。

C#では、メソッドを仮想としてマークして、オーバーライドするpossibleにする必要があります。

ほとんどの場合、クラスを継承できる方法がわからないため、C#では、すべてのメソッドを仮想にマークする必要があることを意味しますか(オーバーライドしたくないいくつかのメソッドを除く)。

72

C#では、メソッドを仮想としてマークして、オーバーライドできるようにする必要があります。ほとんどの場合、クラスを継承できる方法がわからないため、C#ですべてのメソッドを仮想にマークする必要があることを意味しますか(オーバーライドしたくないいくつかのメソッドを除く)。

いいえ。言語設計者が仮想がデフォルトであるはずだと考えた場合、それがデフォルトだったはずです

オーバーライド可能性は機能であり、すべての機能と同様にコストです。オーバーライド可能なメソッドのコストはかなり高くなります。特に、クラスに対する「感度」がある場合、設計、実装、およびテストのコストが大きくなります。仮想メソッドは、テストされていないサードパーティのコードをシステムに導入する方法であり、セキュリティに影響を与えます。

クラスを継承する方法がわからない場合は、クラスを公開しないでくださいため拡張性モデルは、事前に知っておくべきものです。設計とテストの戦略に深く影響するはずです。

私はすべてのクラスがsealedであり、すべてのメソッドがnon-virtualであることを提唱します開封したり、メソッドを仮想化したりする実際の顧客中心の理由が見つかるまで。

基本的にあなたの質問は「私の顧客が私のクラスをどのように利用するつもりなのかを知らないので、それを任意に拡張できるようにすべきですか?」です。番号;あなたは知っている必要があります! 「顧客がクラスをどのように使用するのかわからないので、すべてのプロパティを読み書き可能にする必要がありますか。また、すべてのメソッドをデリゲート型のプロパティの読み書き可能にし、ユーザーがどのメソッドも独自の実装に置き換えることができますか?」いいえ、ユーザーが実際にその機能を必要としていることをevidenceするまで、これらのことは行わないでください!ユーザーが実際に必要とする機能の設計、テスト、および実装に貴重な時間を費やし、知識の立場からそうする。

136
Eric Lippert

私の意見では、現在受け入れられている answer は不必要に独断的です。

事実は、メソッドをvirtualとしてマークしない場合、他の人はその動作をオーバーライドできず、クラスをsealedとしてマークする場合、他の人はクラスから継承できません。これはかなりの痛みを引き起こす可能性があります。クラスにsealedをマークするため、またはメソッドにvirtualをマークしないためにAPIを何度使用したかはわかりません。

理論的には、オーバーライドおよび継承されることを意図したオーバーライドメソッドと継承クラスのみを許可することが正しいアプローチである可能性がありますが、実際にはすべての可能なシナリオを予測することは不可能であり、そのように閉じ込められる十分な理由はありません。

  1. 特別な理由がない場合は、クラスをsealedとしてマークしないでください。
  2. ライブラリが他のユーザーによって使用されることを意図している場合は、少なくともbehaviourを含むクラスのメインメソッドをvirtualとしてマークしてください。

呼び出しを行う1つの方法は、メソッドまたはプロパティの名前を調べることです。リストのGetLength()メソッドは、名前が意味することを正確に実行し、の多くは許可しません解釈。その実装の変更はあまり透過的ではない可能性が高いため、virtualとしてマークする必要はおそらくありません。 Addメソッドを仮想としてマークすると、Addメソッドなどを介して一部のオブジェクトのみを受け入れる特別なリストを作成できるため、はるかに便利です。別の例として、カスタムコントロールがあります。メインの描画メソッドをvirtualにして、他の人が動作の大部分を使用して外観を変更できるようにする必要がありますが、XプロパティとYプロパティはオーバーライドしないでしょう。

最後に、あなたはしばしばその決定をすぐに行う必要はありません。とにかく簡単にコードを変更できる内部プロジェクトでは、これらのことについて心配する必要はありません。メソッドをオーバーライドする必要がある場合は、これが発生したときにいつでもメソッドを仮想化できます。逆に、プロジェクトが他の人によって消費され、更新が遅いAPIまたはライブラリである場合、どのクラスとメソッドが有用であるかを考えることは確かに報われます。この場合、完全に閉鎖するよりも、開放するほうがよいと思います。

24
Patrick Klug

番号!クラスがどのように継承されるかがわからないため、オーバーライドすることを知っている場合は、メソッドをvirtualとしてマークする必要がありますonly

22
John Saunders

いいえ。仮想クラスにする必要があるのは、派生クラスで指定するメソッドのみです。

バーチャルはファイナルとは関係ありません。

C#での仮想メソッドのオーバーライドを防ぐには、sealedを使用します

public class MyClass
{
    public sealed override void MyFinalMethod() {...}
}
5
nunespascal

どちらかの陣営の理由をもう一度思い付くことができますが、それはまったく役に立ちません。

Javaには、意図しない非最終的なパブリックメソッドが数百万ありますが、ホラーストーリーはほとんどありません。

C#には何百万もの封印されたパブリックメソッドがあり、ホラーストーリーはほとんど聞こえません。

したがって、大したことではありません。パブリックメソッドをオーバーライドする必要はほとんどないため、どちらにしても意味がありません。


これは私に別の引数を思い出させます-ローカル変数がデフォルトでfinalであるべきかどうか。それはかなり良い考えですが、それがどれほど価値があるかを誇張することはできません。最終的なものになる可能性はありますが、最終的なものではない何十億ものローカル変数がありますが、実際の問題であることが示されています。

4
irreputable

メソッドを仮想化すると、通常、メソッドを呼び出す必要があるコードの速度が低下します。この速度低下はわずかですが、場合によっては非常に大きくなる可能性があります(とりわけ、非仮想メソッド呼び出しがインライン化され、オプティマイザが不要な操作を排除できるため)。仮想呼び出しが実行速度に影響を与える可能性がある範囲を予測することは常に可能であるとは限らず、そうすることで識別できる利点がある場合を除いて、コードを遅くするようなことは一般的に無効にする必要があります。

メソッドを非仮想化することのパフォーマンス上の利点は、多くの場合、デフォルトでメソッドを非仮想化することを正当化するにはおそらく十分ですが、クラスが継承されるように設計されている場合、ほとんどのメソッドは仮想化され、封印されていないはずです。非仮想メソッドまたはシールされたメソッドの主な使用法は、他の(保護されている可能性がある)仮想メソッドのラッパーとして使用する必要があります(基になる動作を変更するコードは、ラッパーではなく適切な仮想メソッドをオーバーライドする必要があります)。

クラスをsealedとしてマークしたり、継承をアセンブリ内の他のクラスに制限したりすることには、パフォーマンスに関連しない理由がよくあります。特に、クラスが外部継承可能な場合、protectedスコープを持つすべてのメンバーがそのパブリックAPIに効果的に追加され、その動作への変更基本クラス内は、派生したその動作に依存するクラス。一方、クラスが継承可能である場合、そのメソッドをvirtualにしても、露出は実際には増加しません。どちらかといえば、派生クラスにもはや関係のない基本クラス実装の側面を完全に「埋める」ことができるようにすることで、基本クラスの内部への派生クラスの依存を減らすことができます。 List<T>のメンバーが仮想である場合、それらをすべてオーバーライドする派生クラスは配列の配列を使用してオブジェクトを保持でき(ラージオブジェクトヒープの問題を回避)、プライベート配列を保持する必要がありません。配列の配列と一致するList<T>によって使用されます。

1
supercat

はい、そうすべきです。他のほとんどの回答とは異なる回答に回答したいと思います。これはC#の欠陥です。欠陥。その設計の間違い。 Javaと比較すると、別の方法で指定されていない限り(「最終」)、すべてのメソッドが「仮想」であることがわかります。もちろん、メソッド「 Area」で、マージンのある「Rectangle」を表す独自のクラスが必要です。すべてのプロパティとメソッドを含む既存のクラスを利用し、「margin」のプロパティを追加して、通常の四角形領域の値。Rectangleの領域メソッドが仮想とマークされていない場合、あなたは運命にあります。Rectanglesの配列を取得し、すべての四角形の面積の合計を返すメソッドをイメージしてください。ここで、「正しい」、「セキュリティの問題」、または「テスト」のマークが付いた回答を読み返してください。これらは、無効にできないことと比較すると意味がありません。

他の人が「いいえ」と答えたことに驚かない。 C#の作成者がJavaに基づいて言語を作成しているときに、それを理解できなかったことに驚いています。

0
Elad Lavi

いいえ、すべてのメソッドを仮想としてマークしないでください。クラスを継承する方法を検討する必要があります。クラスを継承してはならない場合は、それをシール済みとしてマークし、明らかにメンバーは仮想であってはなりません。クラスが継承される可能性が高い場合は、動作をオーバーライドする機能を最大化する必要があります。したがって、特に理由がない限り、そのようなクラスのあらゆる場所でvirtualを寛大に使用してください。

0