静的にコンパイルされたクエリ (LINQ to XML)
XmlDocument に対し、LINQ to XML で最も重要なパフォーマンスの利点の 1 つは、XPath のクエリは実行時に解釈する必要がある一方で LINQ to XML のクエリは静的にコンパイルされるという点です。この機能は LINQ to XML に組み込まれているので、追加の手順を実行することなく利用できますが、その違いを理解しておくと、この 2 つの技術のどちらかを選ぶときに役立ちます。このトピックでは、相違点について説明します。
静的にコンパイルされたクエリと XPath
次の例は、指定した名前と指定した値の属性がある子孫要素を取得する方法を示しています。
次に示すのは、これに相当する XPath 式です。
//Address[@Type='Shipping']
XDocument po = XDocument.Load("PurchaseOrders.xml");
IEnumerable<XElement> list1 =
from el in po.Descendants("Address")
where (string)el.Attribute("Type") == "Shipping"
select el;
foreach (XElement el in list1)
Console.WriteLine(el);
Dim po = XDocument.Load("PurchaseOrders.xml")
Dim list1 = From el In po.Descendants("Address")
Where el.@Type = "Shipping"
For Each el In list1
Console.WriteLine(el)
Next
この例のクエリ式は、コンパイラによってメソッドベースのクエリ構文に書き換えられています。メソッドベースのクエリ構文で書かれた次の例では、前の例と同じ結果が生成されます。
XDocument po = XDocument.Load("PurchaseOrders.xml");
IEnumerable<XElement> list1 =
po
.Descendants("Address")
.Where(el => (string)el.Attribute("Type") == "Shipping");
foreach (XElement el in list1)
Console.WriteLine(el);
Dim po = XDocument.Load("PurchaseOrders.xml")
Dim list1 As IEnumerable(Of XElement) = po.Descendants("Address").Where(Function(el) el.@Type = "Shipping")
For Each el In list1
Console.WriteLine(el)
Next
Where メソッドは拡張メソッドです。詳細については、「拡張メソッド (C# プログラミング ガイド)」を参照してください。Where は拡張メソッドであるため、上記のクエリは次のように書かれたかのようにコンパイルされます。
XDocument po = XDocument.Load("PurchaseOrders.xml");
IEnumerable<XElement> list1 =
System.Linq.Enumerable.Where(
po.Descendants("Address"),
el => (string)el.Attribute("Type") == "Shipping");
foreach (XElement el in list1)
Console.WriteLine(el);
Dim po = XDocument.Load("PurchaseOrders.xml")
Dim list1 = Enumerable.Where(po.Descendants("Address"), Function(el) el.@Type = "Shipping")
For Each el In list1
Console.WriteLine(el)
Next
この例では、前の 2 つの例と同じ結果が生成されます。これは、静的にリンクされたメソッド呼び出しにクエリが効果的にコンパイルされたことを示します。これと反復子の遅延実行セマンティクスが組み合わさることで、パフォーマンスが向上します。反復子の遅延実行セマンティクスの詳細については、「LINQ to XML における遅延実行とレイジー評価」を参照してください。
注意
これらは、コンパイラが書き込むコードの例です。実際の実装はこれらの例と若干異なる可能性がありますが、パフォーマンスは同じか類似したものになります。
XmlDocument を使用した XPath 式の実行
次の例では、XmlDocument を使用して前の例と同じ結果を達成します。
XmlReader reader = XmlReader.Create("PurchaseOrders.xml");
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList nl = doc.SelectNodes(".//Address[@Type='Shipping']");
foreach (XmlNode n in nl)
Console.WriteLine(n.OuterXml);
reader.Close();
Dim reader = Xml.XmlReader.Create("PurchaseOrders.xml")
Dim doc As New Xml.XmlDocument()
doc.Load(reader)
Dim nl As Xml.XmlNodeList = doc.SelectNodes(".//Address[@Type='Shipping']")
For Each n As Xml.XmlNode In nl
Console.WriteLine(n.OuterXml)
Next
reader.Close()
このクエリは、LINQ to XML を使用する例と同じ出力を返します。唯一の違いは、出力する XML が LINQ to XML ではインデントされるのに対し、XmlDocument ではインデントされないという点です。
ただし、一般に XmlDocument の方法では、SelectNodes メソッドが呼び出されるたびに内部で次の処理を行わなければならないため、LINQ to XML ほどのパフォーマンスは達成できません。
XPath 式を含んでいる文字列を解析してトークンに分解します。
トークンを検証して、XPath 式が有効であることを確認します。
式を内部式ツリーに変換します。
ノードを反復処理し、式の評価に基づいて結果セットのノードを適切に選択します。
この場合、対応する LINQ to XML のクエリよりも処理量がかなり多くなります。具体的なパフォーマンスの違いはクエリの種類によって異なりますが、一般に LINQ to XML のクエリは処理量が少ないため、XmlDocument を使用して XPath 式を評価するよりも良いパフォーマンスが得られます。