Tech Racho エンジニアの「?」を「!」に。
  • インフラ

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

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

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

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

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

編集部注: その後.NET Frameworkがオープンソース化されましたね。当時は想像もつかなかったことでしょう。

CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。