はじめに
現在、データをツリー形式で表現するデータ構造をいろいろなところで目にする機会があります。例えば、Xmlのデータ構造、Windowsのツリーフォルダ、また開発者の方ですとVisual Studioのソリューションエクスプローラやクラスビューなどがそうです。
これらのようなツリー形式で表現するデータ構造は、CompositeパターンやProxyパターンなどのデザインパターンを利用して作成したTreeElement
クラスを使用することで、管理できるようになります。
TreeElement
の主な機能は
などです。またメンバ名や動作などは、.NET FrameworkクラスライブラリのSystem.Windows.Forms.TreeNode
やSystem.Xml.XmlNode
などを参考に作成していますので、これらのクラスを使用したことのある方は、簡単にTreeElement
の操作と概要を理解することができるでしょう。また、使用したことがない方でもTreeElement
の利用方法を覚えることで、これらのクラスを容易に理解できるようになると思います。
ツリー構造内を移動しながらアクセスし操作するTreeElementVisitorクラス
ツリー構造のデータを扱う場合、処理するまでどのような要素が子要素として追加されるか分からないため、コードで表現しようとした時に読みにくいコードになってしまったり、処理が煩雑になってしまうことがあります。
そのような場合に、ツリー構造内を移動しながら要素を操作することができるTreeElementVisitor
クラスを使用すると、シンプルにそして直感的にツリー構造を操作することができます。概要はSystem.Xml.XmlReader
クラスと似ていますが、TreeElementVisitor
クラスは前方向と後方向への移動、また現在の要素への操作が行えるという点が違います。
詳細については後述のTreeElementVisitor
で説明します。
対象読者
- C#を扱える方。
- デザインパターンに興味のある方。
必要な環境
Visual Studio .NET 2003以上および.NET Framework 1.1以上がインストールされていること。
TreeElementの概要
概要図の通り、TreeElement
クラスは子要素を管理するコレクションを持っており、これらの関連が集まることでツリー構造を表現しています。
またValue
プロパティで値となるObject
型を管理し、Name
プロパティで名前を設定することができます。
Name
プロパティを設定しない場合はValue
プロパティに設定されているObject
のToString
メソッドで返される文字列を利用します。
TreeElementの種類
Windowsのディレクトリ構造を思い出していただくと分かると思いますが、ツリー構造には子要素を持つ要素と、持たない要素、特定の要素へのショートカットを表すリンクが存在します。
これらを表現するためにTreeElement
クラスはKind
プロパティでTreeElementKind
列挙型を保持しています。
TreeElementKind
の各値の概要は次のとおりです。
Composite
……子要素を持つ要素を表します。Simplex
……子要素を持たない要素を表します。Link
……ある要素へのリンクを表します。
これらの値はLink
以外、コンストラクタでのみ設定することができます。
Link
を設定する方法は後述の要素のリンク作成で解説します。
要素の追加
最も基本となる要素の追加方法を見てみましょう。要素の追加にはAdd
メソッドを使用します。
// コンストラクタで値となる Object を初期化 (この例では String 型 ) TreeElement topElement = new TreeElement( "Top" ); TreeElement child1Element = new TreeElement( "Child1" ); TreeElement child2Element = new TreeElement( "Child2" ); TreeElement subChildElement = new TreeElement( "SubChild" ); topElement.Add( child1Element ); topElement.Add( child2Element ); child2Element.Add( subChildElement ); // すべての要素のパスをコンソールへ出力 foreach( TreeElement element in topElement ) { Console.WriteLine( element.FullPath ); } // 出力結果 // // Top // Top/Child1 // Top/Child2 // Top/Child2/SubChild // // また IndentedPath プロパティで // インデントされたパスを取得することもできます。 foreach ( TreeElement element in topElement ) { Console.WriteLine( element.IndentedPath ); } // 出力結果 // // Top // Child1 // Child2 // SubChild //
InvalidOperationException
が発生します。このような場合はClone
メソッドや後述するCopyTo
メソッドを利用して追加します。要素の削除
先ほどの要素の追加で作成したtopElement
変数から要素を削除する例を示します。要素の削除にはRemove
メソッドまたはRemoveChild
メソッドなどを使用します。
child1Element.Remove(); // ツリーの状態 // // Top // Top/Child2 // Top/Child2/SubChild // child2Element.RemoveChild( subChildElement ); // ツリーの状態 // // Top // Top/Child2 //
要素間のコピー
要素の追加の補足で述べた通り、1つのツリー構造内に同じ要素を存在させることはできません。このような場合、CopyTo
メソッドで要素をコピーします。
child1Element.CopyTo( subChildElement ); // ツリーの状態 // // Top // Top/Child1 // Top/Child2 // Top/Child2/SubChild // Top/Child2/SubChild/Child1 //