もう LINQ to XML は要らないけど、LINQ は必要

ExDoc を Silverlight でも動くようにちまちまと書き換えている途中。

moonmile/ExDoc – GitHub
https://github.com/moonmile/ExDoc

既に NUnit を使ってテストコードを書いていたものを MSTest に書き換えています。本当は NUnit のまま動かしたかったのですが、Silverlight 上で動かん…と言うか、MSTest の場合も Siliverlight のクラスライブラリは対象にしていない(???)という感じで、結局、Visual Studio 2010 の単体テストコードの雰囲気を感じるのも兼ねて、MSTest を利用しています。

さて、その中でちょっと面白いテストコードを追加しました。

public void TestEqualTwoValueLinq()
{
	EXDocument doc = new EXDocument();
	doc.LoadXML(@"
<members>
    <person name='masuda' age='40'>masuda tomoaki</person>
    <person name='yamada' age='20'>yamada taro</person>
</members>
");
	var els = from t in doc / "members" / "person"
			  where t % "name" == "masuda"
			     && t % "age" == "40"
			  select t;
	EXElement el = els.First();
	Assert.AreEqual("masuda tomoaki", el.Value);
}

ExDoc は、暗黙のキャストと演算子のオーバーライドを派手にやっていて、「==」演算子もオーバーライドして EXElements クラス(要素のコレクションクラス)を返すようにしています。このコレクションは、List で定義してあるので、List ジェネリックのメソッドが使えます…つーか、LINQ の文法に直接載せることができます。

じゃあ、LINQ to XML で記述したほうがいいのではないか?という話は脇において。

ExDoc は、直観的にXMLのツリー構造を辿れることを目的としているので、「doc / “members” / “person”」というのは、XPath で云えば「/members/person」と同じことです。

じゃあ、XPath で書けばいいじゃん!という話は脇において。

属性を比較する場合は、「t % “name” == “masuda”」のように書けるのがミソです。
なので、ExDoc と LINQ を組み合わせて、上のような不思議な文法で書けます。

ちなみに、単一の属性の比較の場合は、一行で書けます。

public void TestEqualValue()
{
	EXDocument doc = new EXDocument();
	doc.LoadXML(@"
<members>
    <person name='masuda'>masuda tomoaki</person>
    <person name='yamada'>yamada taro</person>
</members>
");
	EXElements els = doc / "members" / "person" == "yamada taro";
	Assert.AreEqual(1, els.Count);
	Assert.AreEqual("yamada", (string)(els % "name"));

	EXElement el = doc / "members" / "person" == "yamada taro";
	Assert.AreEqual("yamada", (string)(el % "name"));
}

まぁ、このあたりのワンライナーが作りたかったのが ExDoc の目的です。

ただし、実装的に毎回 foreach で検索して EXElemnt の配列を返しているのでパフォーマンスは良くないんですよね。LINQ の実装と同じく、遅延実行をするようにして

  1. 「/」演算子で取得する部分は、イテレーターの作成のみ
  2. string 型にキャストするか、Value プロパティを参照した時に、探索を実行する。

という工夫が必要なはなずです。まぁ、それは後程。

カテゴリー: 開発, EXDoc パーマリンク

もう LINQ to XML は要らないけど、LINQ は必要 への1件のコメント

  1. masuda のコメント:

    再考すると「where t % “name” == “masuda”」のところの「==」演算子は、LINQ のもので、オーバーライドしたものではない。「%」演算子は t の型(EXElements型)のものを使うけど、「==」演算子は LINQ で解析されたものを使う?のだと思う。ちょっと不思議。
    ちなみ、EXElements 型の「==」演算子は、bool を返さずに EXElements を返すようにしてある極悪品w。なので、if ( t % “name” == “masuda” ) のようなことはできないのだが…うーん、bool 型に暗黙に変換されるようにすれば、これも OK なのか?
    LINQ は 「System.Linq.Enumerable.WhereListIterator」という型が作られる。これを真似すると、遅延実行ができるはず(ネストした時に遅くならずにすむ)

コメントは停止中です。