はじめに
現在、データをツリー形式で表現するデータ構造をいろいろなところで目にする機会があります。例えば、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 //


