String.IsNullOrEmpty でメモリ違反?!

先日、Yahoo! のブログに久々のコメントがありました。久々の開発関連のコメントでしたので、何事だろうと思って読んでみたら・・・

>文字列を空文字と比較するには String.IsNullOrEmpty を使え。
>たとえば以下の条件文があるとする。変数 str に文字列が代入され>ている場合のみ処理を実行させたいのであろうが、str = Nothing の場合を考慮していないため、バグを発生させる恐れがある。
>If str <> "" Then
> ・・・・・・
>End If


まったく見当はずれな意見ですね。上記コードはエラーになりませんよ?
むしろ IsNullOrEmpty はループの中で使うとメモリ違反が発生するバグがあって使わない方が良かったはず。

ちゃんと勉強してください。
2010/4/27(火) 午後 9:35 [ ヘンリー ]


VB.NETコーディング標準 改訂版


バグにならないのも驚きなら、メモリ違反が発生するとはまったくもって驚きの話であるっ!!
というわけで、さっそく調べてみました。


まずメモリ違反の件

ぐぐると出てきた、なるほど・・・この問題ですね。

Microsoft .NET Framework 2. 0 でビルドされたアプリケーションで、 String.IsNullOrEmpty 関数を呼び出す NullReferenceException 例外が表示されます。 この問題は、次の条件に該当する場合に発生します。
・NULL パラメーターを使用して、 String.IsNullOrEmpty 関数を呼び出しており、関数は、ループ内で使用します。
・既定のビルド設定でリリース モードでアプリケーションをビルドするとします。


.NET Framework 2. 0 に組み込まれているアプリケーションで、String.IsNullOrEmpty 関数を呼び出すと、NullReferenceException の例外が表示されます。


これまったく知りませんでした。教えて頂いて本当に有難うございます。<(_ _)>
(でも NullReferenceException って、「メモリ違反」とは微妙に違う気がするのだが・・・)

で、さらに調べると .NET Framework 2.0 Service Pack 1 でこの問題は修正されているようです。

この資料では、Microsoft .NET Framework 2.0 Service Pack 1 (SP1) で修正される問題の一覧を掲載しています。
・・・中略・・・
940900 [FIX] .NET Framework 2.0 でビルドされたアプリケーションで String.IsNullOrEmpty 関数を呼び出すと、NullReferenceException 例外が発生する



.NET Framework 2.0 Service Pack 1 で修正される問題


この資料、読めば読むほど凄いバグの数ですね。VS2005 使っているなら
Service Pack のインストールは必須
と思われます。
また VisualStudio も 2008 以降のバージョンを使うなら Framework の標準バージョンは 3.5 以降ですし、仮に Framework のバージョンで 2.0 を指定するとしても修正されたバージョンを使うわけですから、String.IsNullOrEmpty のバグはもう気にしなくてもいいでしょう。


次に String の比較演算子の件

お次は VB.NET における文字列の比較演算子の問題です。
これも知らなかったのですが、VB.NET の 比較演算子は String 型変数が Nothing の場合、String.Empty と等価で判定するようですね。以下のコードを実行すると、確かにコマンドラインに出力されます。


以下、VB の比較演算子に関するドキュメントから抜粋です。

関係比較演算子を使った型宣言を省略したプログラミング
Option Strict On では、Object 式には関係比較演算子を使用できません。Option Strict が Off の場合、および、expression1 または expression2 が Object 式の場合、実行時の型によって比較方法が決まります。次の表は、式を比較する方法および比較した結果をオペランドのランタイム型に応じて示したものです。

オペランドの種類 比較の種類
両方が String の場合 文字列の並べ替え特性に基づいて並べ替え比較が行われます。
両方が数値の場合 オブジェクトが Double に変換され、数値比較
一方が数値、他方が String の場合 文字列は倍精度浮動小数点数型 (Double) に変換され、数値比較が行われます。文字列型 (String) を倍精度浮動小数点数型 (Double) に変換できない場合は、InvalidCastException がスローされます。
一方または両方が文字列以外の参照型の場合 InvalidCastException がスローされます。


数値の比較では、Nothing を 0 として扱います。文字列比較では、Nothing は "" (空白の文字列) として扱います。


比較演算子 (Visual Basic)


これ VB 独自の仕様のようですね。
このドキュメント、一見 Option Strict Off のときだけの話にも読めますが、Option Strict On でも同様です。
これって一見有難いようですが、他言語と比べると納得いかない仕様であったりもします。実際 C# では


だとコンソールには出力されません。「value != ""」 です。

VB での開発しか考慮していなければ、比較演算子を使えばいいでしょう。しかし将来 VB のコードを他言語に移植することを考えた場合、想定外のバグを生む原因になりかねません。これならむしろ String.IsNullOrEmpty を明示的に使った方がいい。


というわけで、ご忠告はまことに有難いのですが、現在進捗中のプロジェクトは C# に移行も考慮していますので、私は引き続き String.IsNullOrEmpty を使うことにいたします。