14

.Net フレームワークの多数のクラスは「封印済み」としてマークされているため、これらのクラスを独自のクラスに継承することはできません。確かにこれは、既存のオブジェクトの動作を拡張および再定義できるオブジェクト指向の性質に反します。

「封印された」キーワードが存在する正当な理由はありますか?

例として、Silverlight の NotifyCollectionChangedEventArgs は封印されています。AddRange と RemoveRange をサポートする ObservableCollection の独自のバージョンを作成したかったのですが、NCCEA の Silverlight バージョンには、既に IList として定義されている NewItems プロパティと OldItems プロパティの複数の項目をサポートするコンストラクターが用意されていません。通常、NewItems プロパティと OldItems プロパティをオーバーライドする独自のバリアントを使用してクラスを拡張するだけですが、この場合はそれができず、そうすべき理由もわかりません。

4

9 に答える 9

24

拡張可能なクラス (またはフレームワーク) を設計することは簡単ではありません。簡単に言えば、継承はオブジェクト指向プログラミングの唯一の原則ではありません。

開発sealed者/デザイナーがそれらの意図を表現および保持できるようにするために存在します。クラスを封印すると、メンテナンスの負担が軽減され、生活が楽になります。元の開発者は、クラス (またはフレームワーク) の拡張方法を制御できるため、他のコードへの変更を壊すことを心配することなく、内部の変更を加えることができます。

1 つの原則として、開発者はデフォルトですべてのリーフクラスを封印する必要があります。次に、開発者が意図的に非シール クラスを作成すると、拡張性について考える必要があります。


参照: Eric Lippert -フレームワーク クラスの多くが封印されているのはなぜですか?

于 2009-02-16T22:59:33.767 に答える
7

今日私が尋ねたやや関連する質問からのこの回答は、クラスを封印する目的を明確にするのに役立つかもしれません:

自分自身の再利用可能なライブラリに取り組み始めるまで、同じ質問をしていることに気づきました。多くの場合、実装者からのあいまいな、または難解な一連の呼び出しを必要とせずに拡張できない特定のクラスにたどり着きます。

クラスの拡張を許可する場合、開発者が私のクラスを拡張し、この新しいクラスを私のライブラリに渡す場合、この新しいクラスを透過的に操作できますか? この新しいクラスで適切に作業できますか? この新しいクラスは本当に同じように動作しますか?

ほとんどの場合、.Net Framework のシールされたクラスには、あなたが気付いていない特定の内部要件があり、現在の実装をサブクラスに安全に公開できないことがわかりました。

Liskov 置換と合成

そのリンクをたどって、実際の著者に賛成票を投じてください。

于 2009-02-16T22:50:15.863 に答える
2

簡単に言えば、マイクロソフトがそう言ったからです。より長い答えは、Microsoft が拡張メソッドと呼ばれるシールされたクラスを拡張するメカニズムを提供したということです。

一般に、ソース コードがないクラスを拡張することはお勧めできません。たとえば、基本メソッドの呼び出しがオブジェクトの内部データに対して何をするかわかりません。はい、リフレクターなどを使用してそれを把握できますが、一般的には合成または拡張メソッドを使用する方がはるかに優れています。

また、継承が実際に何であるかを考慮する必要があります。クラスを変更するだけでなく、ポリモーフィズムも提供します。たとえば、文字列クラスのセマンティクスを変更した場合、新しい文字列クラスを、文字列が特定の方法で動作することを期待するオブジェクトに渡すとどうなるでしょうか? seal は基本的に、このオブジェクトが常に期待どおりに機能するという契約を強制します。

于 2009-02-16T22:49:58.563 に答える
2

深く掘り下げることなく、ダウンストリームで対処しなければならない潜在的なセキュリティ、保守性、または下位互換性の問題がある場合、Microsoft はクラスを封印することを好むことを理解してください。たとえば、System.Stringセキュリティおよびパフォーマンス上の理由から封印されています。

この特定のケースでは、Microsoft の開発者に、なぜそのクラスを封印することにしたのかを尋ねる必要があります。ただし、最近読んだアーキテクチャ ガイダンスの文献では、「拡張が必要になることがわかっている場合を除き、封印する」というアプローチを支持する傾向があります。この文献では、可能な場合は拡張メソッドを使用する傾向があります。(私はそれに同意すると言っているのではありません。私が最近読んでいるものだと言っているだけです。)

クラスが封印されていなかったとしても、問題のプロパティが仮想化されていない可能性があります。

あなたの特定のシナリオでは、独自の名前を持つ拡張メソッドを使用します。それはあなたができるすべてについてです。

于 2009-02-16T22:55:07.083 に答える
1

私は、無知な-errを保護する以外に、クラスを封印する正当な理由がないことを提案します。つまり、無実です。あなたは古いことわざを知っています、「彼らに十分なロープを与えれば、彼らは彼ら自身を吊るすでしょう」。私が言うには、彼らを揺さぶらせてください。おそらくこれは私のC++のバックグラウンドですが、勤勉でなければ、物事を完全に詰め込む力があることを知っているので、非常に快適です。

私はインターフェースでプログラムする傾向があります。インターフェースはもちろん公開されており、インターフェースによって表現された契約に準拠した独自の実装を誰でも自由に提供できます。これらのインターフェースを実装する私の具体的なクラスは、私的な関心事である傾向があり、内部および/または私的とマークされています。私はそのようなクラスを封印する必要があるとは感じていません。

コードの再利用が望ましい場合は、継承による再利用を避け、構成やその他の手法を優先します。

シーリングは、単純な古いデータ型と見なされる型でも有効な場合がありますが、これについては確信が持てません。

于 2009-02-16T23:33:34.200 に答える
1

.NET がおそらく封印しすぎるという点には同意しますが、一般的には、エコシステムの完全性を保護するためのものです。フレームワーク/API/ランタイム全体の安定性を維持することが最優先事項の 1 つであり、クラス間の相互依存関係がいくぶん壊れやすい場合は、人々がその動作をオーバーライドしてコア機能を不注意に不安定にしないようにするのが最も安全かもしれません。

繰り返しになりますが、.NET チームがあまりにも多くのクラスを封印していると感じる傾向があります。シーリングは、適切なクラス設計は手間がかかりすぎるため、開発者側の単純な怠惰になる場合があります。

于 2009-02-16T22:50:02.400 に答える
0

seal キーワードが有用であることがわかった実用的な用途の 1 つは、「壊れやすいクラスの問題」を回避することです。これは、壊れやすい基本クラスの問題の 1 つを示すビデオですhttp://www.youtube.com/watch?v=xnA3RUJcyY4

少し詳しく説明させてください。

仮想メソッド「Insert」を持つ「DbParent」と呼ばれる親クラスがある以下のシナリオを考えてみましょう。

クラスDbParent {

    public virtual void Insert()
    {
        Console.WriteLine("Parent insert");
    }

}

以下は、さまざまな場所にインストールされた独自の「挿入」実装を持つ単純な子クラスです。

  class DbChildClass : DbParent
  {
  public void  Insert()
    {
        Console.WriteLine("Child inserts");
    }

}

ここで、親クラスの開発者が子クラスへの影響を理解せずに数か月の展開を行った後、新しいメソッド「追加」を追加したとします。この「追加」メソッドは、「挿入」メソッドを内部的に呼び出します (以下は同じコード スニペットです)。

  class DbParent
  {
  public  void Add() // Adds method with out consulting
  {
  this.Insert();
  }

  public virtual void Insert()
  {
        Console.WriteLine("Parent insert");
  }
  }

子クラス オブジェクトを作成して「Add」メソッドを呼び出すクライアント プログラムは、「Child」クラスの「Insert」実装が呼び出されることを想定しています。

  DbChildClass o = new DbChildClass();
  o.Add();
  Console.Read();

しかし、whoaaaa、以下のコードを実行すると、予期しない親クラス「Insert」が呼び出されることがわかります。

そのため、そのような問題を回避するために、クラスを「封印」としてマークします。

于 2013-07-27T12:28:41.933 に答える
0

何かがオブジェクトであるからといって、その動作を拡張または再定義するために常に広く開放されるべきであるとは限りません。

良い例えは、ドアとロックです。ドアの性質は、人々がそこを通過できるようにすることであり、それがドアの目的です。しかし、ほとんどのドアは、人々がドアを通過する能力を制限する可能性のあるロック付きで設計されています。ドアにロックがあるかどうか、およびそのロックがデフォルトでロックされているかどうかの決定は、ドアであるという事実ではなく、部屋の中に何があり、誰がアクセスできるかに基づいて、部屋の設計者に任されています。

もちろん、特定の建物のほとんどのドアがデフォルトでロックされている場合、特に本当に通過したいドアがある場合は、イライラする可能性があります. :-) 私はそこに行ったことがあり、同じ質問をしました。簡単に言えば、複雑なフレームワークを設計するとき、拡張が必要な​​明示的なシナリオがない限り、人々はより慎重な側で少し過ちを犯し、デフォルトでクラスを封印する傾向があるということです。

于 2009-02-16T23:13:29.287 に答える
0

それによって異なりますが、単にインスタンス化することを意図したクラスがあり (継承が存在する場合、実装を単純化するためだけに使用されます)、他のクラスは特定の実装を提供するために継承することを意図しています。

Sealed クラスにはいくつかの利点があります。

  • 仮想メソッドがないため、「例外セーフ」で実装されたメソッドのオーバーライドについて心配する必要はありません。
  • クラスが不変である場合、不変性を維持および保証できます。

それ以外の場合は、シールされたクラスを「快適な」メソッドで装飾したい場合は、拡張メソッド (C# 3.0) を使用します。

于 2009-02-16T23:00:43.733 に答える