元のクイズはここです。
http://blogs.wankuma.com/mnow/archive/2007/10/09/100780.aspx
このクイズをやってみようと思ったのは 刈歩 菜良 さんの勉強会や以下のスレからです。
http://blogs.wankuma.com/carbonara/archive/2007/10/09/100622.aspx
初心者には分かりやすく「小学生は2から3は引けないんだよみたいに」教えておいて、
後でほんとのことを教えてあげればいいんです。
正しさを追い求めるあまり、複雑になりすぎて理解ができないと本末転倒
確かにそのとおりです。
そこでstringの正しいことをおさらいしておいて初心者を導いてもらおうと思いました。
1)一つ目の迷う点「==」
参照が等価であることを確認するには ReferenceEquals を使用します。
値が等価であることを確認するには Equals を使用します。
ややこしいのは「==」の振る舞いについてです。
デフォルトの実装やMicrosoft推奨の実装をしている場合(public static bool operator == できるので)以下のようになります。
演算子 == は、2 つの参照が同じオブジェクトを示すかどうかを確認して参照の等価をテストします。
インスタンスに含まれているデータを変更できない場合、その型は、変更不可能なオブジェクトとして、同じ値を持つ限り同一と見なされるので、参照の等価の代わりに値の等価を比較するように演算子 == をオーバーロードするのが有効です。
Object の 「==」は ReferenceEquals です。
string は 「インスタンスに含まれているデータを変更できない」 ので Equals です。
したがって、CheckString の 一つ目は ReferenceEquals 二つ目三つ目は Equals を実行しています。
2)二つ目の迷う点 「リテラル文字列」
リテラル文字列は、インターン プール(リテラル文字列プール)に参照がおかれます。
動的に生成された文字列は、インターン プールに参照がおかれません。
String.Intern メソッド は、インターン プールの等しい文字列を検索します。
そのような文字列が存在する場合はインターンプール内の該当する参照が返されます。
文字列が存在しない場合は参照がインターン プールに追加された後その参照が返されます。
この、インターン プール は アプリケーション ドメイン AppDomain が管理しています。
アプリケーション ドメイン は、一般に一つの EXE ファイルと 複数の DLL ファイルをロードして実行します。
EXE ファイルや DLL ファイルをロードする時にインターン プールの更新も行われます。
string の定数演算は Debug ビルド かどうかにかかわらず最適化されます。
A・Bは動的に生成された文字列で、インターン プールを検索しませんから、リテラル文字列と同じ参照にはなりません。
C・D・Eはリテラル文字列でインターン プールに参照がおかれていますので、同じ参照値を持っています。
A・B・C・D・Eとも値は「かるぼなら」ですので値は一緒です。
解答
A
False
True
True
B
False
True
True
C
True
True
True
D
True
True
True
E
True
True
True