6

私はそれstringが不変であり、StringBuilder可変であることを知っています。しかし、次のコード出力を説明できる人はいますか? どちらも参照型なので、結果が異なるのはなぜですか?

String s1 = "hello";
String s2 = "hello";
Console.WriteLine(s1 == s2); //true
Console.WriteLine(Object.ReferenceEquals(s1, s2)); //true

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false
Console.WriteLine(Object.ReferenceEquals(sb1,  sb2)); //false
4

4 に答える 4

10

どちらも参照型なので、なぜ結果が異なるのでしょうか?

stringオブジェクトは高度に最適化されているためです。特に、それらは不変であるため、重複を防ぐためにコンパイラーによってインターンされる可能性があります。

stringまったく同じ文字列を表す2 つの異なるオブジェクトがある場合(例のように)、コンパイラはそれを認識し、実際の文字列オブジェクトのインスタンスを 1 つだけ保持します。

その結果、コンパイラに関する限り、s1とオブジェクトは実際には同じオブジェクトであり、メモリ内の同じ場所を参照することさえあります。s2

この簿記は、「インターン テーブル」と呼ばれるものの舞台裏で行われますが、実際に心配する必要はありません。重要なことは、すべての文字列リテラルがデフォルトでコンパイラによってインターンされることです。

オブジェクトは不変ではないため、同じようなことは起こりません。StringBuilder文字列オブジェクトを変更できるように設計されているため、最適化はあまり意味がありません。そのため、オブジェクトsb1sb2オブジェクトは実際には 2 つの異なるオブジェクトとして認識されます。

経験則は非常に単純です。stringデフォルトで使用するか、単一の不変文字列が必要な場合に使用します。同じ文字列を複数回変更する場合、たとえばループやその他の比較的短いコード セクションでのみ使用StringBuilderします。

関連資料: C# 文字列パフォーマンスの最適化

于 2012-05-19T02:40:32.753 に答える
4

宣言すると

String s1 = "hello";
String s2 = "hello";

コンパイラは、2 つの文字列が同一である (そして常に同一である) ことを認識できるほどスマートであるため、1 回だけ格納し、同じ物理メモリのエイリアスとしておよび"hello"を作成します。後で等しいかどうかをテストすると、これら 2 つは本質的に同じ変数であるため、等しくなります。s1s2

一方、あなたが宣言するとき

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");

コンパイラは 2 つの変数を作成します (どちらも変更可能ですが、たまたま同じ値に初期化されるため)。文字列"hello"をそれぞれにコピーしますが、後でそれぞれが変更される可能性があるため、2 つのコピーがあります。そのため、内容は同じですが、物理メモリの異なる場所に存在する 2 つの異なるエンティティであるため、オブジェクトの等価性のテストは失敗します。

于 2012-05-19T02:43:23.813 に答える
3

デフォルトでは、2 つの参照型オブジェクトを比較すると、結果は両方の参照が等しい場合にのみtrueになります。つまり、両方のオペランドがオブジェクトの同じインスタンスを参照する必要があります。sb1 と sb2 によって参照されるオブジェクトは 2 つの異なるオブジェクトであるため、StringBuilders の比較の結果は false です。

しかし、 String クラスは、オブジェクトを参照ではなく値で比較するような方法で等値演算子をオーバーライドします。これは、この動作が非常に直感的であり、プログラマーが期待しているためです。これが、s1 == s2 が true を返す理由を説明しています。

Object.ReferenceEquals(s1, s2) も true を返す (ただし、s1 と s2 は文字列の異なるインスタンスを参照しているように見える) 理由は、文字列インターニングと呼ばれます。これにより、CLR は同一の文字列リテラル (この例では "hallo" や "hallo" など) のすべての出現をアプリケーションごとに 1 回だけ文字列の内部プールに配置するため、s1 と s2 の両方が実際には文字列 "hallo" の同じインスタンスを参照します。 . 文字列は不変であるため、これは可能です。

于 2012-05-19T03:10:22.927 に答える
1

以下を実行しているときは、値ではなく 2 つの異なる StringBuilder オブジェクトを比較しています。

StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = new StringBuilder("hello");
Console.WriteLine(sb1 == sb2); //false

これは、それらの値を比較する方法です。

Console.WriteLine(sb1.ToString() == sb2.ToString());
于 2012-05-19T02:40:07.017 に答える