クラスを封印する理由


96

.Netフレームワークのシールされたクラスの大部分の背後にある動機は何ですか。クラスを封印する利点は何ですか?継承を許可しないことがどのように役立つか、そしておそらくこれらのクラスと戦っている唯一の人ではないかもしれません。

では、なぜフレームワークはこのように設計されているのか、そしてすべてを開封するという画期的な変更ではないのでしょうか?別の理由があるに違いないが、ただ悪であるのか?



回答:


41
  • クラスは貴重すぎる場合があり、継承するように設計されていない場合があります。
  • ランタイム/リフレクションは、型を探すときに、封印されたクラスに関する継承の仮定を行うことができます。これの良い例は-ルックアップの実行速度のために属性をシールすることをお勧めします。type.GetCustomAttributes(typeof(MyAttribute))は、MyAttributeがシールされている場合、パフォーマンスが大幅に向上します。

このトピックに関するMSDNの記事は、クラスのシーリングによる拡張性の制限です。


3
彼らが明確に「注意して使用する」と今言っているのを見てうれしいです...
mmiika 2008年

13
それは私に悪いアドバイスのように見えます:(
Jon Skeet

4
@CVertex:申し訳ありませんが、私はあなたを批判するつもりはありませんでした。記事だけです。
Jon Skeet、

16
@generalt:継承の設計または継承の禁止を信じています。継承の設計にはかなりの作業が必要であり、将来の実装を制限することがよくあります。継承は、呼び出し側に正確に何が呼び出されるかについて、呼び出し側に不確実性ももたらします。また、それは不変性(私が好きです)とうまく混合しません。私はクラス継承が(インターフェイスが大好きなのに対して)比較的少数の場所でのみ役立つと思います。
Jon Skeet

1
@CVertex .NETを使用したことがあり、問題に遭遇して気づかなかった場合、ほぼすべての.NETコアクラスが封印されています。
CoryG 2016

103

クラスは継承のために設計するか、継承を禁止する必要があります。継承の設計にはコストがかかります。

  • それはあなたの実装をピン留めすることができます(ユーザーが一方をオーバーライドするが他方をオーバーライドしない場合、どのメソッドが他のどのメソッドを呼び出すかを宣言する必要があります)
  • 効果だけでなく実装を明らかにする
  • それはあなたがデザインするときより多くの可能性を考えなければならないことを意味します
  • Equalsのようなものは継承ツリーで設計するのが難しい
  • より多くのドキュメントが必要です
  • サブクラス化された不変の型が変更可能になる(ick)

これについては、効果的なJavaの項目17で詳しく説明します。Javaのコンテキストで記述されているという事実に関係なく、アドバイスは.NETにも適用されます。

個人的には、クラスが.NETでデフォルトでシールされていることを望みます。


26
うーん..クラスを拡張する場合、それを壊すのはあなたの問題ではありませんか?
mmiika 2008年

25
基本クラスで制御できない実装の変更が原因で壊れた場合はどうなりますか?それは誰のせいですか?継承は基本的に脆弱性をもたらします。継承よりも構成を優先すると、堅牢性が向上します(IMO)。
Jon Skeet、

6
はい、インターフェースはすばらしいです。そして、はい、とにかく構成を優先できます。しかし、慎重に検討せずに封印されていない基本クラスを公開すると、変更によって派生クラスがうまく機能しなくなる可能性があることを期待する必要があります。それは私にとって悪いことのように感じます。IMO、クラスを封印して破損を回避することをお勧めします。
Jon Skeet、

8
@Joan:構成は「is-a」ではなく「has-a」関係です。したがって、リストのように機能するクラスとそうでないクラスを作成する場合は、List <T>から派生するのではなく、List <T>メンバー変数を使用してクラスを作成することができます。次に、リストを使用してさまざまなメソッドを実装します。
ジョンスキート、

3
@ThunderGr:しかし、それが私のポイントです。サブクラスによって提供される他の実装について心配する必要がない場合は、独自の実装を使用するほうが自由です。たとえば、あるメソッドが別のメソッドを呼び出すことによって実装され、両方が仮想であるとします。ドキュメント化する必要があります- 実装の詳細のように感じますが。基本的に、継承のための設計は手錠を追加します。たくさんの仮想メソッドを持つ封印されていないクラスよりも、インターフェースを実装する封印されたクラスが欲しいです。
Jon Skeet 2013年

8

と思われるシール上のMicrosoftの公式ガイドラインは、この質問は、9年前〜尋ねると、彼らはオプトアウト(デフォルトで密封していない)にオプトインの哲学(デフォルトでシール)から移動してから進化してきました:

X正当な理由がない限り、クラスを封印しないでください。

拡張性シナリオを考えることができないためにクラスを封印することは、正当な理由ではありません。フレームワークのユーザーは、便利なメンバーを追加するなど、さまざまな自明ではない理由でクラスから継承することを好みます。ユーザーが型から継承したい明白でない理由の例については、封印されていないクラスを参照してください。

クラスを封印する正当な理由は次のとおりです。

  • クラスは静的クラスです。静的クラスの設計を参照してください。
  • このクラスは、セキュリティ上重要な秘密を継承された保護メンバーに格納します。
  • クラスは多くの仮想メンバーを継承し、それらを個別にシールするコストは、クラスをシールしないままにしておくことの利点を上回ります。
  • クラスは、非常に高速なランタイムルックアップを必要とする属性です。封印された属性は、封印されていない属性よりもわずかに高いパフォーマンスレベルを持っています。属性を参照してください。

Xシールされた型では、保護されたメンバーまたは仮想メンバーを宣言しないでください

定義により、シールされた型は継承できません。つまり、シールされた型の保護されたメンバーは呼び出せず、シールされた型の仮想メソッドはオーバーライドできません。

オーバーライドするシーリングメンバーを検討します。バーチャルメンバー(バーチャルメンバーで説明)の導入によって発生する可能性のある問題は、オーバーライドにも当てはまりますが、程度はやや低くなります。オーバーライドを封印すると、継承階層のその時点から始まるこれらの問題から保護されます。

実際、ASP.Net Core codebase検索するsealed class、約30のオカレンスしか見つかりません。そのほとんどは属性とテストクラスです。

不変性の保全は封印を支持する良い議論だと私は思います。


4

私はmsdnドキュメントでこの文を見つけました。「シールクラスは主に派生を防ぐために使用されます。これらは基本クラスとして使用できないため、実行時の最適化によってシールクラスメンバーの呼び出しがわずかに速くなる場合があります。」

パフォーマンスが密封されたクラスの唯一の利点であるかどうかはわかりませんが、個人的には他の理由も知りたいです...


4
彼らが話しているパフォーマンスの利点の種類を確認するのは興味深いでしょう...
mmiika

3

たとえば、パフォーマンスは重要な要素です。たとえば、Javaの文字列クラスはfinal(<-Sealed)であり、その理由はパフォーマンスのみです。別の重要なポイントは、ここで詳細に説明されている脆弱な基本クラスの問題を回避することだと思います:http : //blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes。 aspx

フレームワークを提供する場合、レガシープロジェクトの保守性にとって重要であり、脆弱な基本クラスの問題を回避するためにフレームワークをアップグレードすることが重要です


javaのStringが最終的な理由はパフォーマンスではなく、セキュリティです。
CesarB 2008年

@CesarB:はい。ただし、Stringは通常のJavaクラスではありません。これは、演算子のオーバーロードをサポートするJavaの唯一の(私は信じている)クラスです(詳細についてここ、セクション「CとJavaでも(ハードコードされた)演算子のオーバーロードがある」を参照してください)。これは通常のクラスでは不可能です。このためString、たとえそれが最終的でなかったとしても、クラスはサブクラス化さえできないかもしれません。
wchargin 2012年

1

Sealedは、「もろい基本クラスの問題」を防ぐために使用されます。私はそれを説明する良い記事をMSDNで見つけました。


0

シーリングにより、いくつかのマイナーなパフォーマンスの向上を実現できます。これは、JITやレイジーペシミゼーションの世界では、C ++などの世界よりも当てはまりませんが、.NETはペシミゼーションほど良くないので、Javaコンパイラーは主に異なる設計哲学のため、依然として有用です。これは、vtableを介して間接的に呼び出すのではなく、仮想メソッドを直接呼び出すことができることをコンパイラーに伝えます。

また、平等比較などの「閉じた世界」が必要な場合にも重要です。通常、仮想メソッドを定義すると、実際にそのアイデアを実装する等価比較の概念を定義することにかなり夢中になります。一方、仮想メソッドを使用して、クラスの特定のサブクラスに対してそれを定義できる場合があります。そのクラスを封印することで、平等が本当に成り立つことが保証されます。



0

クラス、メソッド、またはプロパティをシールするかどうかを決定するには、一般的に次の2つの点を考慮する必要があります。

•クラスをカスタマイズする機能を通じて、派生クラスが得る潜在的な利点。

•派生クラスがクラスを変更して、正しく機能しなくなったり、期待どおりに機能しなくなる可能性。


0

さらに考慮すべきことは、単体テストでシールされたクラスをスタブ化できないことです。Microsoftのドキュメントから:

封印されたクラスまたは静的メソッドは、スタブ型が仮想メソッドのディスパッチに依存しているため、スタブできません。このような場合は、「shimを使用してアプリケーションを他のアセンブリから分離して単体テストを行う」で説明されているように、shimタイプを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.