6

このコードスニペットは、深さのC#からのものです

    static bool AreReferencesEqual<T>(T first, T second)
        where T : class
    {
        return first == second;
    }

    static void Main()
    {
        string name = "Jon";
        string intro1 = "My name is " + name;
        string intro2 = "My name is " + name;
        Console.WriteLine(intro1 == intro2);
        Console.WriteLine(AreReferencesEqual(intro1, intro2));
    }

上記のコードスニペットの出力は次のとおりです。

True 
False

mainメソッドがに変更されたとき

    static void Main()
    {
        string intro1 = "My name is Jon";
        string intro2 = "My name is Jon";
        Console.WriteLine(intro1 == intro2);
        Console.WriteLine(AreReferencesEqual(intro1, intro2));
    }

上記のコードスニペットの出力は次のとおりです。

True 
True

なぜ理解できないのですか?

編集:文字列インターンを理解したら、次の質問は当てはまりません。

AreReferencesEqual2番目のコードスニペット のジェネリックメソッドでパラメーターはどのように受け取られますか?

==演算子がString型のオーバーロードされたEqualsメソッドを呼び出さないように連結すると、文字列型はどう変わりますか?

4

3 に答える 3

13

文字列の場合、おそらく参照等価性を使用するつもりはありません。ジェネリック メソッドで等値と不等値にアクセスするには、次のようにするのが最善の方法です。

EqualityComparer<T>.Default.Equals(x,y); // for equality
Comparer<T>.Default.Compare(x,y); // for inequality

すなわち

static bool AreValuesEqual<T>(T first, T second)
    where T : class
{
    return EqualityComparer<T>.Default.Equals(first,second);
}

これは引き続きオーバーロードされた を使用しますEqualsが、null なども処理します。不等式の場合、これはヌル、および と の両方IComparable<T>を処理しIComparableます。

その他の演算子については、MiscUtilを参照してください。


再質問; の場合:

    string intro1 = "My name is Jon";
    string intro2 = "My name is Jon";
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

コンパイラとランタイムは文字列を効率的に扱うように設計されているため、trueが得られます。true使用するリテラルはすべて「インターン」され、同じインスタンスが AppDomain で毎回使用されます。可能であれば、(ランタイムではなく)コンパイラも連結を行います。

    string intro1 = "My name is " + "Jon";
    string intro2 = "My name is " + "Jon";
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

前の例とまったく同じコードです。まったく違いはありません。ただし、実行時に文字列を連結するように強制すると、それらは短命である可能性が高いと想定されるため、インターン/再利用されません。したがって、次の場合:

    string name = "Jon";
    string intro1 = "My name is " + name;
    string intro2 = "My name is " + name;
    Console.WriteLine(intro1 == intro2);
    Console.WriteLine(AreReferencesEqual(intro1, intro2));

あなたは4本の弦を持っています。"Jon" (インターン)、"My name is " (インターン)、および"My name is Jon" の2 つの異なるインスタンス。したがって==、true を返し、参照等価は false を返します。ただし、値の等価性 ( EqualityComparer<T>.Default) は true を返します。

于 2009-06-30T04:23:44.163 に答える
5

今日、新しいことを学びました。

ジョンが質問の 1 つで言ったと思いますが、私は答えようとしました。

連結を使用して文字列を作成すると、== は一致する値の 2 つの文字列に対して true を返しますが、それらは同じ参照を指していません (これは、文字列のインターンが原因であると思いました。Jon は、文字列のインターンが定数またはリテラル)。

汎用バージョンでは、object.ReferenceEquals を呼び出しています (これは == とは異なります。文字列の場合、== は値の比較を行います)。

その結果、連結バージョンは false を返しますが、定数 (リテラル文字列) バージョンは true を返します。

編集: ジョンはこれをより良い方法で説明するために周りにいるに違いないと思います:)
怠惰な私、私は本を購入しましたが、まだ始めていません. :(

于 2009-06-30T04:03:43.487 に答える
2

ジェネリックメソッドとは関係ありませんが、文字列のインスタンス化です

あなたが持っているメインの最初のバージョンでは:

string name = "Jon";
string intro1 = "My name is " + name;
string intro2 = "My name is " + name;

4 つの文字列を作成します。そのうちの 2 つは "Jon" と "My name is" というコンパイル時の定数ですが、intro1 と intro2 を初期化するとき、コンパイラは name が常に jon であるとは言えず、ランタイム値を解決して intro1 と intro2 のそれぞれに新しい文字列を作成します。

2番目のバージョンで

string intro1 = "My name is Jon";
string intro2 = "My name is Jon";

文字列は 1 つだけで、それはコンパイル時の定数です。「私の名前はジョンです」。その文字列を intro1 と intro2 の両方に割り当てます。これがその理由です。

AreReferencesEqual(intro1, intro2)

最初のケースでは false を返し、2 番目のケースでは true を返します

于 2009-06-30T05:59:49.800 に答える