БлогNot. Простой шаблон класса дерева на C#

Простой шаблон класса дерева на C#

Консольное приложение "решёток", показывающее, что при реализации "древовидного" шаблона средствами хоть STL, хоть встроенных в .NET System.Collections, "само дерево"-то и не нужно, достаточно реализации функциональности узла TreeNode.

Эту реализацию, собственно, и показывает файл TreeNode.cs, а ExampleOfMain.cs содержит примеры формирования дерева, обхода с выводом в консоль (отступы соблюдаются), поиска и удаления узла.

Ниже приводятся исходники файлов и проект, проверенный в актуальной сборке Visual Studio 2019.

Файл TreeNode.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace CSharpTree {
 public class TreeNode<T> : IEnumerable<TreeNode<T>> { //Узел дерева
  public T Data { get; set; } //Данные
  public TreeNode<T> Parent { get; set; } //Родитель
  public ICollection<TreeNode<T>> Children { get; set; } //Потомки
  public Boolean IsRoot { //Это корень?
   get { return Parent == null; }
  }
  public Boolean IsLeaf { //Это лист?
   get { return Children.Count == 0; }
  }
  public int Level { //Уровень в дереве?
   get {
    if (this.IsRoot) return 0;
    return Parent.Level + 1;
   }
  }
  public TreeNode(T data) { //Конструктор
   this.Data = data;
   this.Children = new LinkedList<TreeNode<T>>();
   this.ElementsIndex = new LinkedList<TreeNode<T>>();
   this.ElementsIndex.Add(this);
  }
  public TreeNode<T> AddChild (T child) { //Добавить потомка
   TreeNode<T> childNode = new TreeNode<T> (child) { Parent = this };
   this.Children.Add(childNode);
   this.RegisterChildForSearch(childNode);
   return childNode;
  }
  public bool RemoveChild (TreeNode<T> childNode) { //Удалить потомка
   bool result = this.Children.Remove (childNode);
   this.UnRegisterChildForSearch (childNode);
   return result;
  }
  public override string ToString() { //Преобразовать в строку
   return Data != null ? Data.ToString() : "[data null]";
  }

  #region searching 
  //Просто дополнительная обёртка-сворачивалка, методы для поиска
  private ICollection<TreeNode<T>> ElementsIndex { get; set; }
  private void RegisterChildForSearch(TreeNode<T> node) {
   ElementsIndex.Add(node);
   if (Parent != null) Parent.RegisterChildForSearch(node);
  }
  private void UnRegisterChildForSearch (TreeNode<T> node) {
   ElementsIndex.Remove (node);
   if (Parent != null) Parent.UnRegisterChildForSearch (node);
  }
  public TreeNode<T> FindTreeNode(Func<TreeNode<T>, bool> predicate) {
   return this.ElementsIndex.FirstOrDefault(predicate);
  }
  #endregion
  #region iterating
  //Ещё один регион, переборщик
  IEnumerator IEnumerable.GetEnumerator() {
   return GetEnumerator();
  }
  public IEnumerator<TreeNode<T>> GetEnumerator() {
   yield return this;
   foreach (var directChild in this.Children) {
    foreach (var anyChild in directChild)
     yield return anyChild;
    }
   }
  #endregion
 }
}
Файл ExampleOfMain.cs
using System;
using System.Text;

namespace CSharpTree {
 class ExampleOfMain {
  static void Main (string [] args) {
   
   //Добавить узлы, для наглядности - дополнительные операторные скобки
   TreeNode<string> root = new TreeNode<string> ("root");
   {
    TreeNode<string> node0 = root.AddChild ("node0");
    TreeNode<string> node1 = root.AddChild ("node1");
    TreeNode<string> node2 = root.AddChild ("node2");
    {
     TreeNode<string> node20 = node2.AddChild (null); //пустой тоже можно
     TreeNode<string> node21 = node2.AddChild ("node21");
     {
      TreeNode<string> node210 = node21.AddChild ("node210");
      TreeNode<string> node211 = node21.AddChild ("node211");
     }
    }
    TreeNode<string> node3 = root.AddChild ("node3");
    {
     TreeNode<string> node30 = node3.AddChild ("node30");
    }
   }

   //Показать обход
   ShowTree (root);

   //Показать поиск
   string nameForSearch = "210";
   Console.WriteLine ("Search for: " + nameForSearch);
   TreeNode<string> found = 
     root.FindTreeNode (node => node.Data != null && node.Data.Contains (nameForSearch));
   Console.WriteLine ("Found: " + found);

   //Показать удаление
   string nameForDeleting = "node2";
   Console.WriteLine ("Deleing item "+ nameForDeleting);
   TreeNode <string> deleted =
     root.FindTreeNode (node => node.Data != null && node.Data == nameForDeleting);
   if (deleted != default && !deleted.IsRoot) {
    deleted.Parent.RemoveChild (deleted);
    Console.WriteLine ("Tree after deleting");
    ShowTree (root);
   }
   else Console.WriteLine ("Can't delete item " + nameForDeleting);

   Console.ReadKey ();
  }
  private static void ShowTree (TreeNode<string> root) {
   //Служебный метод для обхода дерева
   foreach (TreeNode<string> node in root) {
    string indent = CreateIndent (node.Level);
    Console.WriteLine (indent + ( node.Data ?? "null" ));
   }
  }
   private static String CreateIndent (int depth) {
   //Служебный метод для создания отступа при выводе в консоль
   StringBuilder sb = new StringBuilder ();
   for (int i = 0; i < depth; i++) sb.Append (' ');
   return sb.ToString ();
  }
 }
}

 Скачать это решение Visual Studio в архиве .zip (5 Кб)

27.02.2021, 17:54 [1492 просмотра]


теги: c# программирование

К этой статье пока нет комментариев, Ваш будет первым