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がオープンソース化されましたね。当時は想像もつかなかったことでしょう。

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

baba

ゆとりプログラマー。 高校時代から趣味でプログラミングを初め、そのままコードを書き続けて現在に至る。慶應義塾大学環境情報学部(SFC)卒業。BPS設立初期に在学中から参加している最古参メンバーの一人。Ruby on Rails、PHP、Androidアプリ、Windows/Macアプリ、超縦書の開発などを気まぐれにやる。軽度の資格マニアで、情報処理技術者試験(16区分17回 + 情報処理安全確保支援士試験)、技術士(情報工学部門)、Ruby Programmer Gold、AWSソリューションアーキテクト(アソシエイト)、日商簿記2級、漢検準1級などを保有。

babaの書いた記事

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ