Reference Sorceを読む。 ItemBlock - 1Page
ItemContainerGeneratorは文字どおりItemContainerを生成する役割のクラスでしたが、
生成するにあたりコンテナーを入れ子として管理しているItemBlock及び、ItemBlockを継承した
RealizedItemBlockとUnrealizedItemBlockについて書いていきます。
(この辺りの進め方がゴチャゴチャして申し訳ありませんが、ある程度まとまったらまとめ記事を書きますのでご了承ください)
今回はItemBlock、RealizedItemBlock、UnrealizedItemBlockの3つセットで進めていきます。
Reference Source
概要
//------------------------------------------------------ // // Private Nested Classes // //------------------------------------------------------ // The ItemContainerGenerator uses the following data structure to maintain // the correspondence between items and their containers. It's a doubly-linked // list of ItemBlocks, with a sentinel node serving as the header. // Each node maintains two counts: the number of items it holds, and // the number of containers. // // There are two kinds of blocks - one holding only "realized" items (i.e. // items that have been generated into containers) and one holding only // unrealized items. The container count of a realized block is the same // as its item count (one container per item); the container count of an // unrealized block is zero. // // Unrealized blocks can hold any number of items. We only need to know // the count. Realized blocks have a fixed-sized array (BlockSize) so // they hold up to that many items and their corresponding containers. When // a realized block fills up, it inserts a new (empty) realized block into // the list and carries on. // // This data structure was chosen with virtualization in mind. The typical // state is a long block of unrealized items (the ones that have scrolled // off the top), followed by a moderate number (<50?) of realized items // (the ones in view), followed by another long block of unrealized items // (the ones that have not yet scrolled into view). So the list will contain // an unrealized block, followed by 3 or 4 realized blocks, followed by // another unrealized block. Fewer than 10 blocks altogether, so linear // searching won't cost that much. Thus we don't need a more sophisticated // data structure. (If profiling reveals that we do, we can always replace // this one. It's totally private to the ItemContainerGenerator and its // Generators.) // represents a block of items private class ItemBlock // represents a block of unrealized (ungenerated) items private class UnrealizedItemBlock : ItemBlock // represents a block of realized (generated) items private class RealizedItemBlock : ItemBlock
3つともprivateなのはItemContainerGeneratorのネストクラスだからですね。
概要コメントが長いですが、要約すると
アイテムとコンテナ間の関係性を管理する機能を提供します。またブロックは2種類(生成済ブロック / 未生成なブロック)存在しており、
目的としては仮想化を実現するために2種類のブロックが存在しています。 ちなみに生成済ブロックがRealizedItemBlock、未生成ブロックがUnrealizedItemBlockですね。
ItemBlockが提供している役割としてはItemBlock同士の繋がりを表現しています。 ソースを交えながら見ていきます。
private class ItemBlock { public const int BlockSize = 16; public int ItemCount { get { return _count; } set { _count = value; } } public ItemBlock Prev { get { return _prev; } set { _prev = value; } } public ItemBlock Next { get { return _next; } set { _next = value; } } public virtual int ContainerCount { get { return Int32.MaxValue; } } public virtual DependencyObject ContainerAt(int index) { return null; } public virtual object ItemAt(int index) { return null; }
コレクション等ではなく自身で次の要素(ItemBlock)、前の要素(ItemBlock)の要素を持ち管理しています。 この管理のメリットとしては、イテレーションのように再帰処理などで柔軟に要素に対する処理が可能になることですね。
a - b - c - d
のうちcをRemoveをすると勿論、下記のようになります。
a - b - d
ちなみに実際のRemoveメソッドは
public void Remove() { Prev.Next = Next; Next.Prev = Prev; }
となってます。前後の要素を自身で持っているので参照先を変えれば参照されなくなり、GCの対象となり解放されるという流れでしょうか。
非常にシンプルなロジックではありますが、普段はコレクションを使っているため.NETでも実際にどういう風に管理しているのかを改めて確認すると勉強になりますね。
ちなみに先ほどのabcdの流れで、生成済、非生成のItemBlockが並んでいるので呼び出し元の観点だと。
a(RealizedItemBlock) - b(RealizedItemBlock) - c(RealizedItemBlock) - d(UnrealizedItemBlock) となり、cまで生成済(スクロールバーなどの表示領域)、dは非表示領域にいるため生成されていないという状態がみえたりします。 この辺りはItemContainerGeneretarにて流れを書ければと思います。
というわけでは次の記事ではまたItemContainerGeneretarに戻ります。