c#のラムダ式などを使って閉じ忘れのないhtmlビルダーを作りたい
で、コメントに書いたことをやってみました。
メッソドチェーンは、単線的なのですが、無理やり木構造の生成を行っています。
そのため、単線的なら読みやすいのですが、子をたくさん持つときは可読性は良くないかもしれない。
まぁ、変数作って、newをして、子に加えて・・なんていうのを普通に書くよりはマシですが・・。
あと、チェーンの終わりには、GetRoot()で、一番初めに戻ることを必ずやってください。これが肝なので。
ちなみに、このやり方は、Webからのデータ取得プログラミング言語を作ったよで紹介しているRawlerで、プログラム内から簡単に記述したいということから、思いついた方法です。おかげで、Rawlerはスクレイピングプログラムを動的生成するメタプログラミングが可能になりました。面倒だから記事にしていないけど・・。
このメソッドチェーンを使って木構造を作成するというやり方は、たぶん、WPFでも、Add(),Adds(),GetRoot()を拡張メソッドで用意すれば、WPFの動的生成にも使えてちょっとした感じで便利なはずです。(そういえば、やったことない)
filename
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text;
using System.Linq;
namespace UnitTestProject1
{
public class Node
{
public Node(string tag)
{
Tag = tag;
}
protected Node()
{
}
protected string Tag { get; set; }
public string Class { get; set; }
public string Id { get; set; }
public string Value { get; set; }
public Dictionary<string, string> Attribute { get; set; }
public Node Parent { get; set; }
List<Node> children = new List<Node>();
public List<Node> Children
{
get { return children; }
set { children = value; }
}
public Node GetRoot()
{
Node current = this;
while(true)
{
if(current.Parent !=null)
{
current = current.Parent;
}
else
{
break;
}
}
return current;
}
public Node Add(Node node)
{
node = node.GetRoot();
this.Children.Add(node);
node.Parent = this;
return node;
}
public Node Adds(params Node[] nodes)
{
foreach (var item in nodes)
{
this.Add(item);
}
return this;
}
public Node Adds(IEnumerable<Node> nodes)
{
foreach (var item in nodes)
{
this.Add(item);
}
return this;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
if (string.IsNullOrEmpty(Tag) == false)
{
sb.Append("<" + Tag);
if (string.IsNullOrEmpty(Class) == false) sb.Append(" class =\"" + Class + "\"");
if (string.IsNullOrEmpty(Id)==false) sb.Append(" id = \"" + Id + "\"");
if (this.Attribute != null)
{
foreach (var item in this.Attribute)
{
sb.Append(" " + item.Key + "=\"" + item.Value + "\"");
}
}
sb.Append(">" );
}
sb.Append(Value);
foreach (var item in Children)
{
sb.Append(item.ToString());
}
if (string.IsNullOrEmpty(Tag) == false) sb.Append("</" + Tag + ">");
return sb.ToString();
}
//よく使うものは関数にすると捗る
public Node Div(string className,string id)
{
return Add(new Node("div") { Class = className, Id = id });
}
}
//クラスを継承してしまうのもあり。
public class A_Tag:Node
{
public A_Tag()
{
Tag = "a";
}
public string Href { get; set; }
}
//おまけに拡張メソッドで追加
public static class NodeExtend
{
public static Node A(this Node node,string herf)
{
return node.Add(new A_Tag() { Href = herf });
}
}
//テスト用楽ちんメソッド
public static class TestExtend
{
public static void ConsoleWriteLine(this string text)
{
System.Console.WriteLine(text);
}
}
[TestClass]
public class UnitTest4
{
[TestMethod]
public void TestMethod1()
{
//使うときは、必ず最後にGetRoot()をしてからToString()をすること。
new Node("div") { Id = "test" }.Add(new A_Tag() { Href = "http://*****" }).GetRoot().ToString().ConsoleWriteLine();
new Node("div").Adds(
new Node("div") { Id = "test1" }.Add(new A_Tag() { Href = "http://*****" }),
new Node("div") { Id = "test2" }.Add(new A_Tag() { Href = "http://*****" }),
new Node("div") { Id = "test3" }.Add(new A_Tag() { Href = "http://*****" })
).GetRoot().ToString().ConsoleWriteLine();
new Node("table").Adds(Enumerable.Range(1, 2).Select(n=>new Node("tr")
.Adds(
new Node("th") { Value = n+"_1" },
new Node("td") { Value = n+"_2" }
))).ToString().ConsoleWriteLine();
//こういう風に関数を用意するとこんな感じにも書ける。
new Node("").Div("","").A("http://******").GetRoot().ToString().ConsoleWriteLine();
}
}
}