web-dev-qa-db-ja.com

C#がデフォルトでメソッドを非仮想として実装する理由

Javaとは異なり、C#はデフォルトでメソッドを非仮想関数として扱うのはなぜですか?他の考えられる結果よりもパフォーマンスの問題である可能性が高いですか?

既存のアーキテクチャがもたらすいくつかの利点について、Anders Hejlsbergからのパラグラフを読んだことを思い出します。しかし、副作用はどうですか?デフォルトで非仮想メソッドを持つことは本当に良いトレードオフですか?

105
Burcu Dogan

継承がそれを利用できるようにするには、クラスはdesignedである必要があります。デフォルトでメソッドvirtualを持つことは、クラス内のすべての関数をプラグアウトして別の関数で置き換えることができることを意味しますが、これは実際に良いことではありません。多くの人々は、クラスはデフォルトでsealedであるべきだとさえ考えています。

virtualメソッドも、パフォーマンスにわずかな影響を与える可能性があります。ただし、これが主な理由ではない可能性があります。

98
Mehrdad Afshari

ここで、デフォルトではない仮想が物事を行う正しい方法であるというようなコンセンサスがあるように思われます。私は反対側-実用的な-フェンスの側に降りてくるつもりです。

正当化のほとんどは、古い「私たちがあなたに力を与えるなら、あなたはあなた自身を傷つけるかもしれない」という議論のように私に読みました。プログラマーから?!

継承および/または拡張性のためにライブラリを設計するのに十分な(または十分な時間がない)コーダーは、私が修正または調整しなければならない可能性が高いライブラリを正確に作成したコーダーであるように思えます-正確にオーバーライドする機能が最も役立つライブラリ。

私がこれまでにかまれた回数をはるかに上回らないため、醜い、絶望的な回避策のコードを書く必要があった回数(または使用を放棄して独自の代替ソリューションをロールバックした回数)例:Javaの場合)設計者が考慮していない可能性がある場所をオーバーライドする。

デフォルトでは非仮想なので、私の人生は困難になります。

UPDATE:実際には質問に答えなかったことが[かなり正確に]指摘されました。だから-そして、かなり遅れたことをお詫びして...

「プログラマーよりもプログラムを高く評価する誤った決定がなされたため、C#はデフォルトで非仮想メソッドを実装する」のような簡潔なものを書けるようにしたいと思っていました。 (私はそれがこの質問の他のいくつかの回答に基づいていくらか正当化されると思います-パフォーマンス(時期尚早の最適化、誰か?)、またはクラスの動作の保証など)

ただし、私は自分の意見を述べているだけで、Stack Overflowが望んでいる決定的な答えではないことを理解しています。確かに、私は、最高レベルでの決定的な(しかし役に立たない)答えは次のとおりだと思いました。

言語デザイナーは決断を下したため、デフォルトでは非仮想です。

彼らがその決定を下した正確な理由は私には決してないだろうと思います。 会話の筆記録!

したがって、APIをオーバーライドすることの危険性と継承を明示的に設計する必要性についてのここでの回答とコメントは正しい道にありますが、重要な一時的な側面がすべて欠けているように見えます。バージョン間の契約。そして、私は実際には、プラットフォーム上でのユーザーコードの変更についてではなく、.Net/C#プラットフォームがコードの下で変更できるようにすることについてより懸念していると思います。 (そして彼の「プラグマティック」な視点は、反対側から見ているので、私のものとは正反対です。)

(しかし、デフォルトでvirtualを選択してから、コードベースを介して「final」を実行したのではないでしょうか?おそらくそれはまったく同じではありません。そして、Andersは私より明らかに賢いので、嘘をつきます。)

89
mwardm

メソッドがオーバーライドされ、そのために設計されていない可能性があることを忘れるのは簡単すぎるためです。 C#を使用すると、仮想化する前に考えることができます。これは素晴らしい設計上の決定だと思います。一部の人々(Jon Skeetなど)は、クラスはデフォルトでシールされる必要があるとさえ言っています。

17
Zifre

他の人が言ったことを要約すると、いくつかの理由があります:

1-​​= C#では、構文とセマンティクスにC++から直接導入された多くのものがあります。 C++のデフォルトで仮想ではないメソッドがC#に影響を与えたという事実。

2-すべてのメソッド呼び出しはオブジェクトの仮想テーブルを使用する必要があるため、デフォルトですべてのメソッドを仮想化することはパフォーマンスの問題です。さらに、これにより、Just-In-Timeコンパイラがメソッドをインライン化し、他の種類の最適化を実行する機能が大幅に制限されます。

-最も重要なのは、メソッドがデフォルトで仮想でない場合、クラスの動作を保証できることです。 Javaのように、それらがデフォルトで仮想である場合、派生クラスで何かを実行するようにオーバーライドできるため(もちろん、メソッドおよび/またはクラスfinal)。

Zifreが述べたように、なぜC#言語が一歩進んでクラスをデフォルトでシールしないのか疑問に思う人もいるかもしれません。これは、非常に興味深いトピックである、実装継承の問題に関する議論全体の一部です。

12
Trillian

C#はC++(およびその他)の影響を受けます。 C++は、デフォルトでは動的ディスパッチ(仮想関数)を有効にしません。これに対する(良い?)議論の1つは、「クラス階層のメンバーであるクラスをどのくらいの頻度で実装するか」という質問です。デフォルトで動的ディスパッチを有効にしない別の理由は、メモリーのフットプリントです。 virtual table を指す仮想ポインター(vpointer)がないクラスは、遅延バインディングが有効になっている対応するクラスよりも当然小さくなります。

パフォーマンスの問題は、「はい」または「いいえ」と言うのは簡単ではありません。この理由は、C#でのランタイム最適化であるJust In Time(JIT)コンパイルです。

" 仮想呼び出しの速度.. "に関する同様の別の質問

9
Schildmeijer

単純な理由は、パフォーマンスコストに加えて、設計とメンテナンスのコストです。クラスの設計者は、メソッドが別のクラスによってオーバーライドされたときに何が起こるかを計画する必要があるため、仮想メソッドは非仮想メソッドに比べて追加のコストがかかります。特定のメソッドが内部状態を更新したり、特定の動作を期待したりする場合、これは大きな影響を与えます。次に、派生クラスがその動作を変更したときに何が起こるかを計画する必要があります。そのような状況では、信頼性の高いコードを書くことははるかに困難です。

非仮想メソッドを使用すると、完全に制御できます。うまくいかないことはすべて、元の作者の責任です。コードの方がはるかに簡単です。

5
JaredPar

すべてのC#メソッドが仮想である場合、vtblははるかに大きくなります。

クラスに仮想メソッドが定義されている場合、C#オブジェクトには仮想メソッドしかありません。すべてのオブジェクトがvtblに相当するタイプ情報を持っていることは事実ですが、仮想メソッドが定義されていない場合は、基本のObjectメソッドのみが存在します。

@Tom Hawtin:C++、C#、およびJavaはすべてC言語ファミリの言語です:)

2
Thomas Bratt

Perlのバックグラウンドから来たので、C#は、新しいクラスのすべてのユーザーに背後で潜在的に気づかせることなく、基本クラスの動作を非仮想メソッドを介して拡張および変更する必要があったすべての開発者の破滅を封じたと思います詳細。

ListクラスのAddメソッドを考えます。特定のリストが「追加」されるたびに、開発者がいくつかの潜在的なデータベースの1つを更新したい場合はどうなりますか? 'Add'がデフォルトで仮想であった場合、開発者は、すべてのクライアントコードに通常の 'List'ではなく 'BackedList'であることを通知せずに 'Add'メソッドをオーバーライドする 'BackedList'クラスを開発できます。すべての実用的な目的で、「BackedList」はクライアントコードからの単なる別の「リスト」と見なすことができます。

これは、データベース内の1つ以上のスキーマによってサポートされている1つ以上のリストコンポーネントへのアクセスを提供する可能性がある大きなメインクラスの観点からは理にかなっています。 C#メソッドはデフォルトでは仮想ではないため、メインクラスによって提供されるリストは、単純なIEnumerableまたはICollection、さらにはListインスタンスにすることはできませんが、新しいバージョンを確実にするために、「BackedList」としてクライアントにアドバタイズする必要があります正しいスキーマを更新するために、「追加」操作の呼び出しが行われます。

1
Travis

それは確かにパフォーマンスの問題ではありません。 SunのJavaインタプリタは同じコードを使用してディスパッチし(invokevirtualバイトコード)、HotSpotはfinalかどうかにかかわらずまったく同じコードを生成します。すべてのC#オブジェクト(ただし、構造体ではない)には仮想メソッドがあるため、常にvtbl/runtimeクラスIDが必要になります。C#は「Javaに似た言語」の方言です。C++から派生していることを示唆するのは、正直ではありません。

「継承のために設計するか、それを禁止する」べきだという考えがあります。迅速な解決策を講じる深刻なビジネスケースがある瞬間まで、これは素晴らしいアイデアのように思えます。おそらく、自分が制御していないコードから継承することでしょう。