Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Object型をツリー構造で管理するTreeElementクラス

ツリー構造をSystem.Collections.ArrayListクラスのように手軽に扱う

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2006/07/06 00:00

本稿では、Object型をツリー形式のデータ構造で表現することのできるTreeElementクラスを紹介します。さらに煩雑になりがちなツリー構造の操作を容易にするTreeElementVisitorというツリー構造内を自由に移動しながら操作することのできるクラスを紹介します。

目次

はじめに

 現在、データをツリー形式で表現するデータ構造をいろいろなところで目にする機会があります。例えば、Xmlのデータ構造、Windowsのツリーフォルダ、また開発者の方ですとVisual Studioのソリューションエクスプローラやクラスビューなどがそうです。

 これらのようなツリー形式で表現するデータ構造は、CompositeパターンやProxyパターンなどのデザインパターンを利用して作成したTreeElementクラスを使用することで、管理できるようになります。

 TreeElementの主な機能は

 などです。またメンバ名や動作などは、.NET FrameworkクラスライブラリのSystem.Windows.Forms.TreeNodeSystem.Xml.XmlNodeなどを参考に作成していますので、これらのクラスを使用したことのある方は、簡単にTreeElementの操作と概要を理解することができるでしょう。また、使用したことがない方でもTreeElementの利用方法を覚えることで、これらのクラスを容易に理解できるようになると思います。

ツリー構造内を移動しながらアクセスし操作するTreeElementVisitorクラス

 ツリー構造のデータを扱う場合、処理するまでどのような要素が子要素として追加されるか分からないため、コードで表現しようとした時に読みにくいコードになってしまったり、処理が煩雑になってしまうことがあります。

 そのような場合に、ツリー構造内を移動しながら要素を操作することができるTreeElementVisitorクラスを使用すると、シンプルにそして直感的にツリー構造を操作することができます。概要はSystem.Xml.XmlReaderクラスと似ていますが、TreeElementVisitorクラスは前方向と後方向への移動、また現在の要素への操作が行えるという点が違います。

 詳細については後述のTreeElementVisitorで説明します。

対象読者

  • C#を扱える方。
  • デザインパターンに興味のある方。

必要な環境

 Visual Studio .NET 2003以上および.NET Framework 1.1以上がインストールされていること。

TreeElementの概要

TreeElement概要図
TreeElement概要図
TreeElementクラス図
TreeElementクラス図

 概要図の通り、TreeElementクラスは子要素を管理するコレクションを持っており、これらの関連が集まることでツリー構造を表現しています。

 またValueプロパティで値となるObject型を管理し、Nameプロパティで名前を設定することができます。

 Nameプロパティを設定しない場合はValueプロパティに設定されているObjectToStringメソッドで返される文字列を利用します。

TreeElementの種類

 Windowsのディレクトリ構造を思い出していただくと分かると思いますが、ツリー構造には子要素を持つ要素と、持たない要素、特定の要素へのショートカットを表すリンクが存在します。

 これらを表現するためにTreeElementクラスはKindプロパティでTreeElementKind列挙型を保持しています。

 TreeElementKindの各値の概要は次のとおりです。

  • Composite……子要素を持つ要素を表します。
  • Simplex……子要素を持たない要素を表します。
  • Link……ある要素へのリンクを表します。

 これらの値はLink以外、コンストラクタでのみ設定することができます。

 Linkを設定する方法は後述の要素のリンク作成で解説します。

要素の追加

 最も基本となる要素の追加方法を見てみましょう。要素の追加にはAddメソッドを使用します。

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メソッドなどを使用します。

RemoveおよびRemoveChildメソッドの使用例
child1Element.Remove();

// ツリーの状態
// 
// Top
// Top/Child2
// Top/Child2/SubChild
// 

child2Element.RemoveChild( subChildElement );

// ツリーの状態
// 
// Top
// Top/Child2
// 

要素間のコピー

 要素の追加の補足で述べた通り、1つのツリー構造内に同じ要素を存在させることはできません。このような場合、CopyToメソッドで要素をコピーします。

CopyToメソッドの使用例
child1Element.CopyTo( subChildElement );

// ツリーの状態
// 
// Top
// Top/Child1
// Top/Child2
// Top/Child2/SubChild
// Top/Child2/SubChild/Child1
// 
補足
 なぜコピーの場合は、追加の際に例外が発生する、2つの要素を構造内に含む操作が行えるかと言うと、コピーの際にコピー元のディープコピーが作成され、別の参照となった要素がコピー先に作成されるためです。

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

修正履歴

  • 2006/07/09 22:52 TreeElementソリューションファイル 内部のソースを一部修正しました。

著者プロフィール

  • 角尾 良太(カクオ リョウタ)

    .NETの楽しさと力強さ、そしてJavaの美しさなど色々な観点からプログラミングの楽しさをたくさんの人と共有できたら良いなと願う.NETプログラマ。 以下のサイトで制約モデルのテストやバリデーションに関するフレームワークライブラリを公開しています。 NKiwi Framework ラ...

All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5