.NETのすべてのクラスがObjectクラスからグローバルに継承されるのはなぜですか?


16

フレームワークに「グローバルルートクラス」アプローチをもたらす利点は、私にとって非常に興味深いものです。簡単な言葉で言えば、.NETフレームワークは、すべてのクラスに適した一般的な機能を持つ1つのルートオブジェクトクラスを持つように設計されました。

今日、私たちは内部使用のための新しいフレームワーク(SAPプラットフォームの下のフレームワーク)を設計しており、私たちは全員2つのキャンプに分かれています。

私は「グローバルルート」キャンプにいます。そして、そのようなアプローチが優れた柔軟性と開発コストの削減をもたらす理由は、一般的な機能をこれ以上開発しないためです。

そのため、どのような理由で.NETアーキテクトがそのような方法でフレームワークを設計するのか、本当に知りたいと思っています。


6
.NET Frameworkは一般化されたソフトウェア開発環境の一部であることを忘れないでください。あなたのようなより具体的なフレームワークは、「ルート」オブジェクトを必要としないかもしれません。ルートがありますobject、それはのようなすべてのオブジェクト間でいくつかの基本的な機能、提供するため、一部では、.NETフレームワークの中でToString()GetHashCode()
ロバート・ハーヴェイの

10
「どのような理由で.NETアーキテクトがそのような方法でフレームワークを設計するようになったのかを知るのは非常に興味深い。それには3つの非常に良い理由があります。1。Javaがそのようにした2. Javaがそのようにした3. Javaがそのようにした
-dasblinkenlight

2
@vcsjones:およびJavaは、たとえばwait()/ notify()/ notifyAll()およびですでに汚染されていclone()ます。
ヨアヒムザウアー

3
@daskblinkenlightそれはうんち。Javaはそれをしませんでした。
ダンテ

2
@dasblinkenlight:参考までに、Javaは現在ラムダ、LINQ関数などを使用してC#をコピーしています...
Mehrdad

回答:


18

最も差し迫った原因Objectは、コンテナ(ジェネリックより前)であり、Cスタイルの「必要なものすべてのためにもう一度書く」のではなく、何でも含めることができます。もちろん、間違いなく、すべてが特定のクラスから継承し、この事実を悪用して型安全性のすべてのスペックを完全に失うという考えは非常に恐ろしいため、ジェネリックなしで言語を出荷する際の大きな警告灯であるはずでしたこれObjectは、新しいコードでは完全に冗長です。


6
これは正しい答えです。一般的なサポートの欠如が唯一の理由でした。共通メソッドは共通である必要がないため、他の「共通メソッド」引数は実際の引数ではありません-例:1)ToString:ほとんどの場合、乱用されます。2)GetHashCode:プログラムの動作に応じて、ほとんどのクラスはこのメソッドを必要としません。3)GetType:インスタンスメソッドである必要はありません
Codism

2
.NETは、すべてが特定のクラスから継承されるべきであるという考えを「乱用」することにより、「タイプセーフのあらゆる点を完全に失う」のはどのような方法でしょうか。C ++でObject記述できなかった記述可能なshitcodeはありvoid *ますか?
Carson63000

3
誰が私void*がより良いと思ったと言ったのですか?そうではありません。どちらかといえば。それはですさらに悪化
DeadMG

void*暗黙的なポインターキャストがすべて行われているCの方が悪いですが、この点に関してはC ++の方が厳密です。少なくとも明示的にキャストする必要があります。
タマスゼレイ

2
@fish:用語のり:Cでは、「暗黙のキャスト」のようなものはありません。キャストは、変換を指定する明示的な演算子です。あなたは「暗黙のポインター変換」を意味します。
キーストンプソン

11

エリック・リッパートは、System.Objectクラスからの継承が「顧客にとって最高の価値」を提供したと言ったことを覚えています。[編集:うん、彼はここで言った ]:

... 共通のベースタイプは必要ありません。この選択は不必要に行われたわけではありません。それは、顧客に最高の価値を提供したいという願いから生まれました。

型システムやその他のことを設計するとき、決定点に達することがあります-XかXでないかを決定する必要があります...発生した金額は、共通の基本タイプを持たないことの純利益よりも大きくなります。そのため、共通のベースタイプを選択します。

それはかなりあいまいな答えです。より具体的な回答が必要な場合は、より具体的な質問をしてみてください。

すべてをSystem.Objectクラスから派生させると、信頼性と有用性が得られます。これは、私がたくさん尊敬するようになりました。すべてのオブジェクトには型(GetType)があり、CLRのファイナライズメソッド(Finalize)に沿っていること、およびGetHashCodeコレクションを処理するときに使用できることを知っています。


2
これには欠陥があります。必要はありません。機能を置き換えるGetTypeだけで拡張できtypeofます。についてFinalizeは、実際には、多くの型が完全にファイナライズ可能ではなく、意味のあるFinalizeメソッドを持ちません。さらに、多くの型はハッシュ可能でもなく、文字列に変換できません。特に、そのようなことを意図したり、公共の使用に渡したりすることのない内部型です。これらのタイプはすべて、インターフェースが不必要に汚染されています。
DeadMG

5
いいえ、それは欠陥がない-私はあなたが使用することを主張したことがないGetTypeFinalizeまたはGetHashCodeすべてのためにSystem.Object。私は単にあなたがそれらを望めばそこにいると述べた。「不必要に汚染されたインターフェース」については、elippertの最初の引用である「顧客にとって最高の価値」を参照します。明らかに、.NETチームはトレードオフに喜んで対処しました。
gws2

必要ない場合は、インターフェイスの汚染です。「したい場合」は、メソッドを含める正当な理由になることはありません。あなたがそれを必要とし、あなたの消費者がそれを呼ぶので、それはただそこにあるべきです。そうでなければ、それは良くありません。また、あなたのリッパーの引用は、権威の誤acyに対する訴えもあります。彼はまた、「それは良い」以外に何も言っていないからです。
DeadMG

私はあなたのポイント@DeadMGを議論しようとはしていません-質問は具体的には「.NETのすべてのクラスがObjectクラスからグローバルに継承する理由」であり、したがって.NETチームに非常に近い誰かによる以前の投稿にリンクしました.NET開発チームがこのアプローチを選んだ理由について、曖昧ではあるが正当な答えを与えたマイクロソフト。
gws2

この質問への答えには曖昧すぎます。
DeadMG

4

JavaとC#のデザイナーはルートオブジェクトを追加したと思います。これは、無料のランチのように、本質的に無料であるためです。

ルートオブジェクトを追加するコストは、最初の仮想関数を追加するコストとほぼ同じです。CLRとJVMはどちらもオブジェクトファイナライザを備えたガベージコレクション環境であるjava.lang.Object.finalizeため、System.Object.Finalizeメソッドまたはメソッドに対して少なくとも1つの仮想が必要です。したがって、ルートオブジェクトを追加するコストは既に前払いされており、料金を支払うことなくすべてのメリットを得ることができます。これは両方の長所です。共通のルートクラスを必要とするユーザーは必要なものを取得し、あまり気にしないユーザーは、あたかも存在しないかのようにプログラムできます。


4

ここに3つの質問があるようです:1つは.NETに共通のルートが導入された理由、2つ目はその長所と短所、3つ目はフレームワーク要素がグローバルであることが良いアイデアかどうかですルート。

.NETに共通のルートがあるのはなぜですか?

最も技術的な意味では、共通のルートを持つことは、リフレクションおよびpre-genericsコンテナに不可欠です。

また、私の知る限り、などの基本法と共通のルートを持つequals()hashCode()Javaで非常に積極的に受信された、と彼らはよくとしてその機能を持っていると思ったので、C#は、(とりわけ)Javaの影響を受けていました。

言語に共通のルートを持つことの長所と短所は何ですか?

共通のルートを持つことの利点:

  • あなたは、許可することができ、すべてのような、オブジェクトはあなたが重要と考える特定の機能を持っているequals()hashCode()。つまり、たとえば、JavaまたはC#のすべてのオブジェクトをハッシュマップで使用できることを意味します。その状況をC ++と比較してください。
  • 未知のタイプのオブジェクトを参照できます-たとえば、汎用のコンテナのように情報を転送するだけの場合。
  • たとえば、リフレクションを使用する場合など、不明なタイプのオブジェクトを参照できます。
  • まれに、異なるタイプのオブジェクトを受け入れることができる場合に使用できますprintf。たとえば、-likeメソッドのパラメーターとして使用できます。
  • クラスを1つ変更するだけで、すべてのオブジェクトの動作を柔軟に変更できます。たとえば、C#プログラムのすべてのオブジェクトにメソッドを追加する場合、拡張メソッドをに追加できますobject。あまり一般的ではないかもしれませんが、共通のルートがなければ不可能です。

共通のルートを持つことの欠点:

  • より正確な型を使用できたとしても、ある値のオブジェクトを参照することは乱用される可能性があります。

フレームワークプロジェクトの共通のルートを使用する必要がありますか?

もちろん非常に主観的ですが、上記の賛否両論のリストを見ると、間違いなくyesと答えます。具体的には、プロリストの最後の箇条書き(後でルートを変更することですべてのオブジェクトの動作を変更できる柔軟性)は、プラットフォームで非常に便利になります。言語。

それに加えて、これはさらに主観的ですが、共通の根という概念は非常にエレガントで魅力的です。また、いくつかのツールの使用が簡単になります。たとえば、ツールに共通ルートのすべての子孫を表示し、フレームワークのタイプの迅速で適切な概要を受け取るように要求することが簡単になりました。

もちろん、そのためにはタイプは少なくともわずかに関連している必要があり、特に、共通のルートを持つためだけに「is-a」ルールを犠牲にすることはありません。


私は、c#は単にJavaの影響を受けたのではなく、ある時点でJavaだったと思います。マイクロソフトはJavaを使用できなくなったため、数十億ドルの投資が失われました。彼らは、すでに新しい名前を持っている言語を与えることから始めました。
マイクブラウン

3

数学的には、topを含む型システムを使用すると、少しエレガントになり、言語をもう少し完全に定義できるようになります。

フレームワークを作成している場合、物事は必然的に既存のコードベースによって消費されます。フレームワークを消費するものには他のすべてのタイプが存在するため、ユニバーサルなスーパータイプを持つことはできません。

そのポイント、共通の基底クラスを持っているの決定は、あなたがやっていることに依存します。多くのものが一般的な行動を持っていることは非常に稀であり、そしてそれだけでは、共通の行動を経由して、これらのものを参照するために有用です。

しかし、それは起こります。もしそうなら、その共通の振る舞いを抽象化することですぐに進んでください。


2

フレームワーク内のすべてのクラスが特定のもの(ハッシュコードの取得、文字列への変換、等価性チェックなど)をサポートすることを望んでいたため、ルートオブジェクトをプッシュしました。C#とJavaの両方が、オブジェクトのこれらの共通機能をすべてルートクラスに入れると便利だと感じました。

OOPの原則などに違反していないことに注意してください。Objectクラスの内容は、システムのサブクラス(読み取り:任意のクラス)で意味を持ちます。この設計を採用する場合は、必ずこのパターンに従ってください。つまり、システム内のすべての単一クラスに属さないものをルートに含めないでください。このルールを注意深く守れば、システム全体に役立つ一般的なコードを含むルートクラスが必要ない理由はわかりません。


2
それはまったく真実ではありません。ハッシュされたり、反映されたり、文字列に変換されたりしない内部専用クラスが多数あります。不必要な継承と不要なメソッド、多くの原則に違反すること間違いありません
DeadMG

2

いくつかの理由、すべての子クラスが共有できる共通機能。また、フレームワークの作成者は、他の多くの機能をすべてが使用できるフレームワークに書き込むことができました。例としては、ASP.NETキャッシングとセッションがあります。オブジェクトを受け入れるためのaddメソッドを作成したため、ほとんどすべてをそれらに格納できます。

ルートクラスは非常に魅力的ですが、誤用は非常に簡単です。代わりにルートインターフェイスを使用することは可能でしょうか?そして、1つまたは2つの小さな方法がありますか?それとも、フレームワークに必要なコードよりもはるかに多くのコードを追加しますか?

あなたが書いているフレームワークのすべての可能なクラスにどの機能を公開する必要があるのか​​興味があります。それは本当にすべてのオブジェクトで使用されますか?またはそれらのほとんどによって?そして、ルートクラスを作成したら、人々がそれにランダムな機能を追加するのをどのように防止しますか?または、「グローバル」にしたいランダム変数?

数年前、非常に大規模なアプリケーションでルートクラスを作成しました。数ヶ月の開発の後、そこにはビジネスのないコードが投入されました。古いASPアプリケーションを変換していましたが、ルートクラスは、以前使用していた古いglobal.incファイルに置き換わりました。そのレッスンを難しい方法で学ばなければなりませんでした。


2

すべてのスタンドアロンヒープオブジェクトはObject、から継承します。すべてのスタンドアロンヒープオブジェクトには、タイプを識別する手段など、特定の共通の側面が必要であるため、これは理にかなっています。そうでなければ、ガベージコレクターが不明なタイプのヒープオブジェクトへの参照を持っている場合、そのオブジェクトに関連付けられたメモリのBLOB内のどのビットが他のヒープオブジェクトへの参照と見なされるべきかを知る方法がありません。

さらに、型システム内では、構造のメンバーとクラスのメンバーを定義するために同じメカニズムを使用すると便利です。値型の格納場所(変数、パラメーター、フィールド、配列スロットなど)の動作は、クラス型の格納場所の動作とは非常に異なりますが、そのような動作の違いは、ソースコードコンパイラーと実行エンジン(型システムで表現されるのではなく、JITコンパイラ)。

この結果の1つは、値の型を定義すると、2つの型(ストレージロケーション型とヒープオブジェクト型)が効果的に定義されることです。前者は暗黙的に後者に変換され、後者は型キャストによって前者に変換されます。両方のタイプの変換は、問題のタイプの1つのインスタンスから別のインスタンスにすべてのパブリックフィールドとプライベートフィールドをコピーすることにより機能します。さらに、汎用制約を使用して、値型の保存場所のインターフェイスメンバを最初にコピーせずに直接呼び出すことができます。

値型ヒープオブジェクトへの参照はクラス参照のように動作し、値型のようには動作しないため、これはすべて重要です。たとえば、次のコードを考えてみましょう。

string testEnumerator <T>(T it)ここで、T:IEnumerator <string>
{
  var it2 = it;
  it.MoveNext();
  it2.MoveNext();
  それを返します。
}
public void test()
{
  var theList = new List <string>();
  theList.Add( "Fred");
  theList.Add( "George");
  theList.Add( "Percy");
  theList.Add( "Molly");
  theList.Add( "Ron");

  var enum1 = theList.GetEnumerator();
  IEnumerator <string> enum2 = enum1;

  Debug.Print(testEnumerator(enum1));
  Debug.Print(testEnumerator(enum1));
  Debug.Print(testEnumerator(enum2));
  Debug.Print(testEnumerator(enum2));
}

場合testEnumerator()メソッドが値型のストレージロケーションを渡され、itそのパブリック及びプライベートフィールド、渡された値からコピーされたインスタンスを受信します。ローカル変数it2は、フィールドがすべてからコピーされる別のインスタンスを保持しますit。を呼び出しMoveNextit2も影響はありませんit

上記のコードにクラスタイプの格納場所が渡されると、渡された値it、およびit2がすべて同じオブジェクトを参照するため、MoveNext()それらのいずれかを呼び出すと、それらすべてに対して効果的に呼び出されます。

キャストList<String>.EnumeratorするIEnumerator<String>と、値型からクラス型に効果的に変わることに注意してください。ヒープオブジェクトのタイプはList<String>.Enumerator異なりますが、その動作は同じ名前の値タイプとは大きく異なります。


ToString()に言及していないための+1
JeffO

1
これは、実装の詳細に関するすべてです。これらをユーザーに公開する必要はありません。
DeadMG

@DeadMG:どういう意味ですか?a List<T>.Enumeratorとして格納されるa の観測可能な動作は、に格納されるList<T>.Enumeratorものの動作とは大きく異なりIEnumerator<T>ます。前者の型の値をいずれかの型の格納場所に格納すると、列挙子の状態のコピーが作成されるため、後者のタイプの値を、同じく後者のタイプの保存場所に保存すると、コピーは作成されません。
-supercat

はい、しかしその事実とObject何の関係もありません
DeadMG

@DeadMG:私のポイントは、typeのヒープインスタンスSystem.Int32ものインスタンスでSystem.Objectあるが、typeの格納場所にはインスタンスもインスタンスへの参照もSystem.Int32保持されないことSystem.Objectです。
supercat

2

この設計は、Smalltalkにまでさかのぼります。Smalltalkは、他のほとんどすべての懸念事項を犠牲にしてオブジェクト指向を追求する試みとして主に見ています。そのため、他の手法がおそらく(または確実に)優れている場合でも、(私の意見では)オブジェクト指向を使用する傾向があります。

Objectルートに(または同様の)単一の階層を持たせることで、コレクションクラスをのコレクションとして作成するのが非常に簡単になります(一例)Object。したがって、コレクションにあらゆる種類のオブジェクトを含めるのは簡単です。

ただし、この比較的小さな利点の見返りとして、多くの欠点があります。最初に、設計の観点から、あなたはいくつかの本当に正気でないアイデアに終わります。少なくともJavaの宇宙観によれば、無神論と森には共通点がありますか?両方ともハッシュコードを持っていること!マップはコレクションですか?Javaによると、そうではありません!

Smalltalkが設計されていた70年代には、このようなナンセンスは受け入れられました。これは、主に誰も合理的な代替案を設計していなかったためです。Smalltalkは1980年に完成しましたが、1983年にはAda(ジェネリックを含む)が設計されました。エイダは予想されるほどの人気を達成したことはありませんでしたが、そのジェネリックは、モノリシック階層に固有の狂気なしに、任意のタイプのオブジェクトのコレクションをサポートするのに十分でした。

Java(および程度は低いが.NET)が設計されたとき、モノリシッククラス階層はおそらく「安全」な選択肢と見なされていました。問題があるが、ほとんどが既知の問題です。対照的に、ジェネリックプログラミングは、ほとんどすべての人が(それでも)問題に対する少なくとも理論的にはるかに優れたアプローチであると認識しものでしたが、 、エイダは大部分が失敗として却下された)。

しかし、明確な話をしましょう。モノリシックな階層構造は間違いでした。その間違いの理由は少なくとも理解できましたが、とにかく間違いでした。それは悪い設計であり、その設計上の問題はそれを使用するほとんどすべてのコードに広がっています。

しかし、今日の新しい設計については、合理的な疑問はありません。モノリシックな階層構造を使用することは明らかな間違いであり、悪い考えです。


.NETが出荷される前に、テンプレートはすばらしく有用であることが知られていたことを述べる価値があります。
DeadMG

商業の世界では、Ada 失敗でしたが、それは冗長性のためではなく、オペレーティングシステムサービスへの標準化されたインターフェイスの欠如のためです。ファイルシステム、グラフィックス、ネットワークなどに対する標準APIがなかったため、「ホストされた」アプリケーションのAda開発が非常に高価になりました。
ケビンクライン

@kevincline:私はおそらく少し違った言い方をするべきでした-Ada全体が市場での失敗により失敗として却下されたという結果に。Adaの使用を拒否することは(IMO)非常に合理的でしたが、Adaから学ぶことを拒否することは(せいぜい)それほどではありません。
ジェリーコフィン

@Jerry:C ++テンプレートはAdaジェネリックのアイデアを採用し、暗黙のインスタンス化で大幅に改善したと思います。
ケビンクライン

1

あなたがしたいことは実際に興味深いです、それが間違っているか、完了するまで正しいかどうかを証明するのは難しいです。

考慮すべき事項を次に示します。

  • このトップレベルのオブジェクトを、より具体的なオブジェクトに意図的に渡すことはありますか?
  • クラスのすべてのクラスにどのコードを配置するつもりですか?

共有ルートからメリットが得られるのは、これらの2つだけです。言語では、いくつかの非常に一般的なメソッドを定義するために使用され、まだ定義されていないがそれらは適用されないオブジェクトを渡すことができます。

また、個人的な経験から:

Smalltalkのバックグラウンドを持つ開発者が作成したツールキットを使用しました。彼は、すべての「データ」クラスが単一のクラスを拡張し、すべてのメソッドがそのクラスを使用するようにしました。問題は、さまざまなデータクラスが常に交換可能ではなかったため、現在、エディターとコンパイラーが特定の状況に何を渡すかについてのヘルプを提供できず、常に役立つとは限らない彼のドキュメントを参照する必要がありました。 。

それは私が今までに対処しなければならなかったライブラリを使用するのが最も困難でした。


0

それがOOPの方法だからです。あらゆるものを参照できる型(オブジェクト)を持つことが重要です。タイプオブジェクトの引数は何でも受け入れます。継承もあり、ToString()、GetHashCode()などがあらゆるもので利用可能であることを確認できます。

Oracleでさえ、このような基本型を持つことの重要性を認識しており、2017年近くにJavaのプリミティブを削除することを計画しています


6
OOPの方法でも重要でもありません。あなたは「それは良い」以外の実際の議論を与えません。
DeadMG

1
@DeadMG最初の文以降の引数を読むことができます。プリミティブはオブジェクトとは異なる動作をするため、プログラマはオブジェクトとプリミティブに対して同じ目的のコードの多くのバリアントを書く必要があります。
ダンテ

2
@DeadMGは、彼はあなたがすべてのオブジェクトを想定しToString()GetType()今後もそうであることを意味すると言った。型の変数を使用してobject任意の.NETオブジェクトを格納できることを指摘する価値があるでしょう。
-JohnL

また、任意の型の参照を保持できるユーザー定義型を作成することも完全に可能です。そのために特別な言語機能は必要ありません。
DeadMG

特定のコードを含むレベルでのみオブジェクトを使用する必要があります。グラフを結合するためだけにオブジェクトを使用することはできません。「オブジェクト」は実際にいくつかの重要なメソッドを実装し、型がまだ作成されていないツールにオブジェクトを渡す方法として機能します。
ビルK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.