Techracho

オーバーライドでちょっとはまりました

このエントリーをはてなブックマーク Share
2009.01.22    C#, 馬場   タグ: , , , , , —    baba   

C#で System.Xml.XmlDocument を使っています。
XmlDocument xmlDocument = new XmlDocument();

プレフィックス付きのノードを作るとき、たとえば


<s:root xmlns:s="urn:hogehoge-hoge" >
    <s:child />
</s:root>

というXMLを作りたいとして、

XmlNode root = xmlDocument.CreateNode(XmlNodeType.Element, "s", "root", "hogehoge-hoge");
XmlNode child = xmlDocument.CreateNode(XmlNodeType.Element, "s", "child", "hogehoge-hoge");
root.AppendChild(child);

とすると動きますが、毎回NamespaceURIを指定するのは変だと思います。
しかし、最後の引数を指定しないと、prefixが出力されません。

また、
xmlDocument.Save("filename");
でファイルに保存するのは1行ですが、string型で受け取ろうと思うと、

StringWriter writer = new StringWriter();
xmlDocument.Save(writer);
string output = writer.ToString();

としないといけません。

色々不便なので、XmlDocumentを継承したExtendedXmlDocumentを作りました。命名がSXGA並にださいですが。。
このクラス内部では、prefixとnamespaceURIのHashtableを持っていて、一度指定すると以降はnamespaceURIを指定しなくても対応するものを用意してくれます。
また、ToString() をオーバーライドしてあるので、1行で文字列を受け取れます。

・・・と、ここまでは良かったんですが(前置きが長い)、
その後調子に乗ってCreateAttribute()をオーバーライドしたらはまりました。

public override XmlAttribute CreateAttribute(string prefix, string name, string value);
を作って、内部で
base.CreateAttribute(name);
を呼ぶと、StackOverflowExceptionが発生します。

どうやら、XmlDocument.CreateAttribute(string) 内部で CreateAttribute(string, string, string) を呼んでいたみたいです。
C#のオーバーライドは動的にメソッドを呼んでくれるので、派生クラスのCreateAttribute(string, string, string) が呼ばれてしまって、その内部で CreateAttribute(string) を呼ぶと無限ループ、と。

.NET Frameworkの内部コードが見られれば一瞬で解決した問題なんですが。
オーバーライドは慎重に使わないとバグの元ですね。オーバーロードと組み合わさるとたまに危険。気をつけます。

COPYRIGHT [C] 2009 BEYOND PERSPECTIVE SOLUTIONS LTD. ALL RIGHTS RESERVED.