[DotNetBook]参照および重芁なデヌタ型、遞択の機胜

この蚘事では、匕き続き䞀連の蚘事を公開したす。その結果、.NET CLRおよび.NET党般の䜜業に関する本になりたす。 IDisposableテヌマは、オヌバヌクロックペンサンプラヌずしお遞択されたした。 次に、タむプの違いを芋おみたしょう。 本党䜓がGitHub DotNetBookで利甚可胜になりたす。 だから問題ずプルリク゚ストは倧歓迎です:)



これは、 Struct / Classの章ずその違いからの抜粋です。



クラス/構造間の遞択の機胜



䞡方のタむプの機胜、それらの長所ず短所に぀いお考え、それらをより適切に䜿甚する堎所を決めたしょう。 ここで、もちろん、遞択は重芁な型に察するものであるずいう声明を出しおいる叀兞を思い出す䟡倀がありたす。私たちの型が継承可胜であるこずを蚈画しおいない堎合、それはその寿呜の間に倉曎されず、そのサむズは16バむトを超えたせん。 しかし、すべおがそれほど明癜ではありたせん。 完党な比范を行うには、さたざたな角床からタむプを遞択し、将来の䜿甚に向けおシナリオを粟神的に考えお考える必芁がありたす。



ご泚意



Habréで公開されおいる章は曎新されおおらず、おそらくすでに叀いものです。 そのため、最新のテキストに぀いおは元に戻しおください。









遞択基準を3぀のグルヌプに分けるこずを提案したす。





ナヌザヌが蚭蚈する各゚ンティティは、その目的を完党に反映する必芁がありたす。 そしお、これはその名前たたは盞互䜜甚むンタヌフェヌスメ゜ッド、プロパティだけでなく、重芁なタむプず参照タむプの遞択さえもアヌキテクチャ䞊の理由で行うこずができたす。 型システムのアヌキテクチャの芳点から、クラスではなく構造を遞択できる理由を説明したしょう。



  1. 投圱された型がその状態のセマンティックロヌドに関しお䞍倉である堎合 、これはその状態が䜕らかのプロセスを完党に反映しおいるか、䜕かの倀であるこずを意味したす。 ぀たり、型のむンスタンスは完党に䞀定であり、本質的に倉曎するこずはできたせん。 オフセットを指定しおこの定数に基づいお型の別のむンスタンスを䜜成するか、プロパティを指定しお最初から䜜成できたす。 しかし、それを倉曎する暩利はありたせん。 構造䜓が䞍倉型であるこずを意味するものではないこずに泚意しおください。 必芁に応じおフィヌルドを倉曎できたす。 それだけでなく、 ref



    パラメヌタヌを介しおメ゜ッドぞの構造ぞのリンクを提䟛し、メ゜ッドを終了するずきに倉曎されたフィヌルドを取埗できたす。 しかし、私は建築の芳点に぀いお話しおいる。 䟋で説明したす。

    • DateTime



      は、ある時点の抂念をカプセル化した構造です。 このデヌタはUInt64



      の圢匏で保存されたすが、特定の時点の個々の特性ぞのアクセスを提䟛したす。 䟋幎、月、日、時間、分、秒、ミリ秒、さらにはプロセッサヌのティック。 ただし、カプセル化するずいう事実に基づいお、本質的に可倉にするこずはできたせん。 特定の瞬間を倉えるこずはできたせん。 私の人生の次の瞬間を子䟛時代の最高の誕生日に生きるこずはできたせん。 時間は倉わらない。 そのため、デヌタ型の遞択は、読み取り専甚の察話むンタヌフェむスプロパティの倉曎ごずに新しいむンスタンスを提䟛するを持぀クラス、たたはむンスタンスのフィヌルドを倉曎する可胜性があるにもかかわらずこれを行うべきではない構造のいずれかになりたす瞬間の説明は*倀*です。 数字のように。 数字の構造に入れお倉曎するこずはできたせんか 元の1日からのオフセットである別の時点を取埗する堎合は、構造の新しいむンスタンスを取埗するだけです。
    • KeyValuePair<TKey, TValue>



      は、接続されたキヌず倀のペアの抂念をカプセル化する構造です。 この構造は、蟞曞の内容を䞀芧衚瀺するずきにナヌザヌに発行するためにのみ䜿甚されるこずに泚意しおください。 なぜアヌキテクチャの芳点から構造が遞択されおいるのですか 答えは簡単です。なぜなら、蟞曞<T>のフレヌムワヌク内では、キヌず意味は分離できない抂念だからです。 はい、内郚はすべお異なりたす。 内郚には、キヌが倀ずは別に存圚する耇雑な構造がありたす。 ただし、倖郚ナヌザヌの堎合、むンタラクションむンタヌフェむスずデヌタ構造自䜓の意味から芋るず、キヌず倀のペアは䞍可分な抂念です。 党䜓が*倀*です。 このキヌで別の倀を芋぀けた堎合、これはペア党䜓が倉曎されたこずを意味したす。 倖郚オブザヌバヌの堎合、個別のキヌはなく、個別の倀もありたせん。それらは単䞀の党䜓です。 そのため、この堎合の構造は理想的なオプションです。
  2. 蚭蚈タむプが倖郚タむプの䞍可欠な郚分である堎合 。 しかし同時に、それは構造的に䞍可欠です。 すなわち 倖郚型がカプセル化されたむンスタンスを指すず蚀うのは間違っおいたすが、カプセル化されたものがそのすべおのプロパティずずもに倖郚の完党な郚分であるこずは絶察に正しいです。 通垞、これは別の構造の䞀郚である構造を蚭蚈するずきに䜿甚されたす。

    • たずえば、ファむルのヘッダヌ構造をずる堎合、あるファむルから別のファむルぞのリンクを䞎えるのは䞍正です。 同様に、ヘッダヌはheader.txt



      ファむルにありたす。 これは、ドキュメントを他の堎所に挿入するずきに適しおいたすが、ファむルを埋め蟌むのではなく、ファむルシステムぞの盞察リンクによっお行いたす。 良い䟋は、Windows OSのショヌトカットファむルです。 ただし、ファむルヘッダヌたずえば、画像サむズ、圧瞮方法、撮圱パラメヌタヌ、GPS座暙、およびその他のメタ情報を瀺すJPEGファむルヘッダヌに぀いお話しおいる堎合は、ヘッダヌの解析に䜿甚されるタむプを蚭蚈するずきに、構造を䜿甚するず非垞に䟿利です。 結局、構造内のすべおのヘッダヌを蚘述したので、ファむル内のすべおのフィヌルドずたったく同じ䜍眮をメモリ内で取埗できたす。 そしお、非盎列*(Header *)readedBuffer



      なしの単玔な安党でない倉換*(Header *)readedBuffer



      完党に満たされたデヌタ構造。
  3. 同時に、各䟋には次のプロパティがあるこずに泚意しおください。 どの䟋にも、䜕かの動䜜を継承するプロパティはありたせん 。 さらに、これらの䟋はすべお、これらの゚ンティティの動䜜を継承するこずにはたったく意味がないこずも瀺しおいたす。 それらは䜕かの単䜍ずしお完党に自絊自足です。

    コヌドの効率の芳点から問題を芋るず、もう䞀方の遞択肢がありたす。

    1. アンマネヌゞコヌドから構造化デヌタを取埗する必芁がある堎合は、構造を遞択する必芁がありたす。 たたは、安党でないメ゜ッドにデヌタ構造を䞎えたす。 参照タむプはこれには適しおいたせん。
    2. タむプがメ゜ッド呌び出しでデヌタを転送するために頻繁に䜿甚される堎合戻り倀たたはメ゜ッドパラメヌタヌずしおも、異なる堎所から同じ倀を参照する必芁がない堎合、遞択は構造です。 䟋ずしお、タプルを䞎えるこずができたす。 メ゜ッドがタプルを介しお耇数の倀を返す堎合、構造䜓ずしお宣蚀されおいるValueTupleを返すこずを意味したす。 すなわち 戻ったずき、このメ゜ッドはヒヌプにメモリを割り圓おたせんが、ストリヌムのスタックを䜿甚したす。メモリの割り圓おはたったくコストがかかりたせん。
    3. 蚭蚈䞭のタむプのむンスタンスに察しおさらにトラフィックを䜜成するシステムを蚭蚈しおいる堎合。 同時に、むンスタンス自䜓のサむズは非垞に小さく、むンスタンスの存続期間は非垞に短いため、参照型を䜿甚するず、オブゞェクトのプヌルが䜿甚されたり、プヌルがない堎合はヒヌプの制埡されないポむ捚おが発生したりしたす。 同時に、䞀郚のオブゞェクトは叀い䞖代に移動し、GCの沈降を匕き起こしたす。 そのような堎所で重芁な型を䜿甚するず可胜な堎合、SOHに䜕も入らないため、パフォヌマンスが向䞊したす。これにより、GCがアンロヌドされ、アルゎリズムが高速に動䜜したす。


䞊蚘のすべおを組み合わせお、構造の䜿甚に関するヒントずコメントを提䟛できたす。

  1. コレクションを遞択するずきは、内郚に倧きな構造がある倧きな配列を避ける必芁がありたす。 これは、配列およびそのほずんどに基づくデヌタ構造にも適甚されたす。 これは、ラヌゞオブゞェクトヒヌプの離脱ずその断片化に぀ながる可胜性がありたす。 構造に4バむトのフィヌルドがある堎合、4バむトかかるず蚈算するだけでは䞍十分です。 たったくありたせん。 32ビットシステムの堎合、構造䜓の各フィヌルドは4バむト各フィヌルドのアドレスは4で割る必芁がありたすで、64ビットシステムでは8バむトで敎列されるこずを理解しおください。 すなわち 配列のサむズは、構造のサむズずアプリケヌションが実行されおいるプラ​​ットフォヌムに䟝存する必芁がありたす。 4バむトの䟋では、85K /フィヌルドあたり4から32バむト*フィヌルド数= 4-配列ヘッダヌのサむズプラットフォヌムに応じお、配列ごずに玄2600の芁玠ただし、圓然ながら、それを削陀する必芁がありたす。 ただ䜕か そんなにない しかし、10,000個の芁玠の魔法の定数がうたく適合するように思えるかもしれたせん
  2. たた、デヌタ゜ヌスずしお十分に倧きなサむズの構造を䜿甚し、それをフィヌルドずしお特定のクラスに配眮するず同時に、同じコピヌが1000コピヌ耇補されるこずを理解するこずも䟡倀がありたす保持するのに䟿利だからですすべおが手元にある堎合、クラスの各むンスタンスを構造のサむズだけ増やしたす。これにより、最終的に第0䞖代が膚匵し、第1䞖代たたは2䞖代になりたす。さらに、クラスのむンスタンスが短呜であり、 ityvaete圌らはれロ䞖代のGCに収集するこずができるずいう事実 - 1ミリ秒、あなたは非垞に圌らが実際に次の1、あるいは第二に埗るこずができたこずに倱望されたす。 実際、違いは䜕ですか 違いは、䞖代0が1ミリ秒で収集される堎合、1番目ず2番目は非垞に遅く、最初から沈䞋するこずです。
  3. ほが同じ理由で、メ゜ッド呌び出しのチェヌンを通じお倧きな構造を匷制するこずは避けおください。 すべおが盞互に呌び出しを開始するず、そのような呌び出しはスタック䞊でより倚くのスペヌスを占有し、 StackOverflowException



    によっおアプリケヌションの寿呜をStackOverflowException



    です。 2番目の理由はパフォヌマンスです。 コピヌが倚いほど、すべおの動䜜が遅くなりたす。




したがっお、䞀般に、デヌタ型の遞択はかなり重芁なプロセスです。 倚くの堎合、これは時期尚早な最適化に起因する可胜性がありたすが、掚奚されたせん。 ただし、状況が䞊蚘の原則に該圓するこずがわかっおいる堎合は、重芁なタむプの方向に安党に遞択できたす。



基本タむプはオブゞェクトであり、むンタヌフェヌスを実装する機胜です。 ボクシング。



私たちは火ず氎の䞡方を経隓したした。どんなむンタビュヌも受けられたす。 おそらく.NET CLRチヌムでも。 ただし、急いでmicrosoft.comず入力しお、空垭セクションを探しおはいけたせん。間に合いたす。 この質問に答えたしょう。 重芁な型にSyncBlockIndexぞの参照たたは仮想メ゜ッドのテヌブルぞのポむンタヌが含たれおいない堎合...それでは、すみたせん、どのように型object



を継承しobject



か 実際、すべおのカノンによれば、どのタむプも圌から継承しおいたす。 残念ながら、この質問に察する答えは1぀の文には含たれたせんが、パズルの最埌のピヌスが最終的に適切な䜍眮に収たるように、型システムに぀いおの理解が埗られたす。

したがっお、メモリ内の重芁なタむプの配眮に぀いおもう䞀床思い出したしょう。 どこにいおも、どこにいおも、圌らがいる堎所に怍え蟌たれたす。 圌らはその䞀郚になりたす。 法埋が小さなたたは倧きなオブゞェクトのヒヌプ内にあり、倀が蚭定されおいる堎所にあるこずを䞻匵する参照型ずは異なり、垞にオブゞェクトのあるヒヌプ内の堎所ぞのリンクを配眮したす。

したがっお、考えおみるず、重芁な型にはメ゜ッドToString



、 Equals



、 GetHashCode



、これらは仮想で再定矩されおいたすが、意味のある型を継承しおメ゜ッドを再定矩するこずはできたせん。 なんで 再定矩可胜なメ゜ッドを䜿甚しお重芁なタむプを実行する堎合、コヌルルヌティングを実行する仮想メ゜ッドのテヌブルが必芁になるためです。 そしお、これは、管理されおいない䞖界ぞの構造の転送の問題を䌎いたす。䜙分なフィヌルドがそこに行きたす。 その結果、重芁なタむプのメ゜ッドの蚘述はどこかにあるこずがわかりたすが、仮想メ゜ッドのテヌブルを介しおそれらに盎接アクセスするこずはできたせん。

これは、継承の欠劂が人為的であるこずを瀺唆しおいたす。



すなわち ある意味、だたされおいるわけではありたせんが、話はしおいたせん。型は非垞に振る舞いが異なりたすが、CLR実装レベルでは、それらの違いはそれほど重芁ではありたせん。 しかし、それに぀いおは埌で。

プログラムに次の行を蚘述した堎合



 var obj = (object)10;
      
      







その埌、 10



番の凊理を停止したす。 いわゆるボクシングが発生したすパッケヌゞング。 すなわち 基本クラスを介しお䜜業できるようになりたす。 そのような機䌚があれば、VMT仮想メ゜ッドのテヌブルが利甚可胜になり、仮想メ゜ッドToString、Equals、GetHashCodeを安党に呌び出すこずができたす。 さらに、元の倀はどこにでも保存できるため、スタック䞊、クラスフィヌルドずしおも、 object



タむプに至るたで、この番号ぞのリンクを氞遠に保存する機䌚が埗られ、実際にはボクシングは重芁なタむプのコピヌを䜜成し、ぞのポむンタヌを䜜成したせんオリゞナル。 すなわち ボクシングが発生した堎合



ご列垭の皆様。 たずもな瀟䌚では、これは蚀うのが習慣ではありたせんが、重芁なタむプの参照バヌゞョンを入手したした。 もう䞀床繰り返したす。ボクシングを行った埌、構造は**参照型ずたったく同じシステムフィヌルドのセット**を取埗し、本栌的な参照型になりたした。 構造はクラスになりたした。 この珟象をDotnetskyのKulbitず呌びたしょう。 この名前はこのようなcな事態にふさわしいず思われたす。

ずころで、私の蚀葉の誠実さを信じるように、特定のむンタヌフェむスを実装する構造このむンタヌフェむス自䜓を䜿甚するずどうなるかを理解するだけで十分です。



 struct Foo : IBoo { int x; void Boo() { x = 666; } } IBoo boo = new Foo(); boo.Boo();
      
      







したがっお、Fooのむンスタンスが䜜成されるず、その倀は基本的にスタック䞊にありたす。 次に、この倉数をむンタヌフェむス型倉数に入れたす。 参照型の倉数ぞの構造。 boxing



起こっおいたす。 いいね 出力では、type object



を取埗したした。 しかし、私たちが持っおいる倉数はむンタヌフェヌス型です。 そしお、これは型倉換が必芁であるこずを意味したす。 すなわち 呌び出しは次のようなものです。



 IBoo boo = (IBoo)(box_to_object)new Foo(); boo.Boo();
      
      







すなわち そのようなコヌドを曞くこずは非垞に非効率的です。 オリゞナルの代わりにコピヌを倉曎するだけでなく

 void Main() { var foo = new Foo(); foo.a = 1; Console.WriteLine(foo.a); // -> 1 IBoo boo = foo; boo.Boo(); //    foo.a  10 Console.WriteLine(foo.a); // -> 1 } struct Foo : IBoo { public int a; public void Boo() { a = 10; } } interface IBoo { void Boo(); }
      
      







二床デマのように芋えたす。 初めお-コヌドを芋るず、 他の誰かのコヌドで䜕を扱っおいるかを知る必芁はなく、以䞋にIBoo



むンタヌフェヌスぞのキャストを芋るIBoo



たす。 実際、Fooは構造䜓ではなくクラスであるず考えるようになりたす。 さらに、構造ずクラスぞの芖芚的な分離が完党にないため、むンタヌフェヌスを介した倉曎の結果がfooに入らなければならないずいう完党な感芚が埗られたす。これは、booはfooのコピヌであるため発生したせん。 実際に私たちを誀解させたす。 私の意芋では、倖郚の開発者が正しく理解できるように、このようなコヌドにはコメントを提䟛する必芁がありたす。

以前の掚論に関連する2番目の芳察結果は、 object



からIBoo



ぞの型キャストを実行できるこずです。 これは、ボックス化された有意な型が特別なものではなく、実際には有意な型の参照バリアントであるずいう別の蚌拠です。 たたは、別の角床から芋るず、型システムのすべおの型が参照されたす。 構造を重芁なものずしお扱い、その䟡倀党䜓を「出荷」できるずいうこずだけです。 C ++の䞖界で蚀うように、オブゞェクトぞのポむンタヌを逆参照したす。

しかし、あなたは反察するこずができたす圌らが蚀うのは、もしすべおが私が蚀うずおりであるなら、あなたはこのような䜕かを曞くこずができるずいうこずです



 var referenceToInteger = (IInt32)10;
      
      







そしお、 object



だけでなく、パックされた意味のある型ぞの型付き参照を取埗しobject



。 しかし、それは重芁なタむプ、友人の党䜓のアむデアを台無しにするでしょう。 そしお、䞻なアむデアは䟡倀の完党性であり、その特性に基づいお優れた最適化を行うこずができたす。 だから、座っおはいけたせん このアむデアを砎壊したしょう



 public sealed class Boxed<T> where T : struct { public T Value; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { return Value.Equals(obj); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return Value.ToString(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return Value.GetHashCode(); } }
      
      







䜕を埗たの ボクシングの完党に類䌌したものが埗られたした。 しかし、むンスタンスメ゜ッドを呌び出すこずで、コンテンツを倉曎できるようになりたした。 そしお、このデヌタ構造ぞのリンクを持っおいる党員がこれらの倉曎を受け取りたす。



 var typedBoxing = new Boxed<int> { Value = 10 }; var pureBoxing = (object)10;
      
      







あなたが芋る最初のオプションは、やや䞍確かに芋えたす。 習慣的な型キャストの代わりに、私たちは半狂乱です。 ケヌスが2行目かどうか。 日本の詩ずしおのラコニック。 ただし、実際にはほが完党に同䞀です。 唯䞀の違いは、通垞のパッキング䞭に、ヒヌプにメモリを割り圓おた埌、メモリのれロクリアが行われないこずです。メモリは必芁な構造をすぐに凊理したす。 䞀方、最初のバリアントではクリヌニングが行われたす。 このため、圓瀟のオプションは埓来のパッケヌゞングよりも10遅くなりたす。

しかし今では、パックされた倀に察しおいく぀かのメ゜ッドを呌び出すこずができたす。



 struct Foo { public int x; public void ChangeTo(int newx) { x = newx; } } var boxed = new Boxed<Foo> { Value = new Foo { x = 5 } }; boxed.Value.ChangeTo(10); var unboxed = boxed.Value;
      
      







新しいツヌルを入手したしたが、それをどうすればいいのかただわかりたせん。 掚論によっお答えを取埗したしょう

  1. Boxed <T>タむプは、通垞のタむプず基本的に同じです。ヒヌプにメモリを割り圓お、そこに倀を䞎え、ある皮のunboxを行うこずでそれを取埗できるようにしたす。
  2. 同様に、パックされた構造ぞの参照を倱うず、GCはそれを収集したす。
  3. ただし、パック型で䜜業する機䌚がありたす。メ゜ッドを呌び出したす。
  4. たた、SOH / LOHの重芁なタむプのむンスタンスを別のむンスタンスに眮き換えるこずができたす。 これたではできたせんでした。 unboxing



    を行い、構造を別のものに倉曎し、 unboxing



    化を元に戻しお、消費者に新しいリンクを配垃する必芁がありたした。
  5. たた、パッケヌゞングの䞻な問題点を考えおみたしょう。 メモリにトラフィックを䜜成したす。 理解できない数のオブゞェクトのトラフィック。その䞀郚は、ガベヌゞコレクションで問題が発生する第1䞖代たで存続できたす。ガベヌゞコレクションで問題が発生したす。 そしお、短呜のオブゞェクトのトラフィックがある堎合、頭に浮かぶ最初の解決策はプルです。 .




 var pool = new Pool<Boxed<Foo>>(maxCount:1000); var boxed = pool.Box(10); boxed.Value=70; // use boxed value here pool.Free(boxed);
      
      







すなわち , . , . , boxed



, . GC.

:





,



 static unsafe void Main() { //  boxed int object boxed = 10; //     VMT var address = (void**)EntityPtr.ToPointerWithOffset(boxed); unsafe { //   Virtual Methods Table var structVmt = typeof(SimpleIntHolder).TypeHandle.Value.ToPointer(); //   VMT  ,   Heap  VMT SimpleIntHolder,  Int   *address = structVmt; } var structure = (IGetterByInterface)boxed; Console.WriteLine(structure.GetByInterface()); } interface IGetterByInterface { int GetByInterface(); } struct SimpleIntHolder : IGetterByInterface { public int value; int IGetterByInterface.GetByInterface() { return value; } }
      
      







, . github . boxing int reference type. :

  1. boxing
  2. ( VMT Int32)
  3. VMT SimpleIntHolder
  4. VMT VMT
  5. unbox
  6. — , Int32, .


, , .














All Articles