2016-05-06 9 views
4

以下のコードはどのように印刷されますtrueObject.ReferenceEqualsは2つの異なるオブジェクトに対してtrueを出力します

string x = new string(new char[0]); 
string y = new string(new char[0]); 
Console.WriteLine(object.ReferenceEquals(x,y)); 

Iは、2つの別々のオブジェクトを構築するために、その後、それらの参照が比較予想されるので、私は、これがFalseを印刷することが期待。

+0

彼らは二つの等しいオブジェクトだから... '' Equals'を上書きしますSTRING'。なぜあなたはそれが 'false'を印刷することを期待しましたか? –

+1

( 'objectを呼び出すことを意味しましたか?参照Equals'の代わりに?Equals'の代わりに?) –

+0

@JonSkeetはい、申し訳ありません、私は実験でコードを変更しました:P –

答えて

4

これは、空の文字配列から空文字列を作成するために特殊なケースが作成されたためです。 (これはchar*パラメータのコンストラクタです)reference source for stringから

string x = new string(new char[0]); 
string y = new string(new char[0]); 
Console.WriteLine(object.ReferenceEquals(x, y)); // true 
Console.WriteLine(object.ReferenceEquals(x, string.Empty)); // true 

:文字列コンストラクタはこのように構成され、空の文字列をstring.Emptyを返すも

[System.Security.SecurityCritical] // auto-generated 
private unsafe String CtorCharPtr(char *ptr) 
{ 
    if (ptr == null) 
     return String.Empty; 

#if !FEATURE_PAL 
    if (ptr < (char*)64000) 
     throw new ArgumentException(Environment.GetResourceString("Arg_MustBeStringPtrNotAtom")); 
#endif // FEATURE_PAL 

    Contract.Assert(this == null, "this == null");  // this is the string constructor, we allocate it 

    try { 
     int count = wcslen(ptr); 
     if (count == 0) 
      return String.Empty; 

     String result = FastAllocateString(count); 
     fixed (char *dest = result) 
      wstrcpy(dest, ptr, count); 
     return result; 
    } 
    catch (NullReferenceException) { 
     throw new ArgumentOutOfRangeException("ptr", Environment.GetResourceString("ArgumentOutOfRange_PartialWCHAR")); 
    } 
} 

そして(これはのコンストラクタですchar[]パラメータ):

[System.Security.SecuritySafeCritical] // auto-generated 
    private String CtorCharArray(char [] value) 
    { 
     if (value != null && value.Length != 0) { 
      String result = FastAllocateString(value.Length); 

      unsafe { 
       fixed (char * dest = result, source = value) { 
        wstrcpy(dest, source, value.Length); 
       } 
      } 
      return result; 
     } 
     else 
      return String.Empty; 
    } 

注ライン:

 if (count == 0) 
      return String.Empty; 

 else 
      return String.Empty; 
+0

彼は通常、非内部オブジェクトを返す文字列コンストラクタを使用します。 –

+0

String.Internを使用すると、出力を変更できますか? –

+0

これは*文字列インターンによるものではなく、通常の文字列インターンではありません。これはコンパイラとは関係ありません。 –

1

object.Equalsは最初に参照の等価性をチェックしてから、最初の変数(x)にEqualsを呼び出します。

(参照に影響する可能性のある現在のカルチャ設定を使用して)文字列の実際の値と照合するので、両方のオブジェクトが同じ値を持つのでtrueを返します。あなたの編集のために


:CLRrは、いくつかの魔法を行い、あなたのnew string(char[0])を評価しようとしているようですので、抑留することができます。 x""に設定すると、同じ動作が確認できます。

+0

質問が変更されたので、この回答は今は無関係です。 –

+0

更新しました@JonSkeet –

+0

コンパイル時のチェックではありません。ここのイリノイはあなたが期待していたものとまったく同じです。 –

4

これは、CLRでの文書化されていない(私が知る限り)最適化です。非常に奇妙ですが、はい:newオペレータが2回の呼び出しから同じ参照を返しています。

これは、CoreCLRでもLinux(さらにはMono)でも実装されているようです。

文字列コンストラクタはこれまで私が見てきた唯一の例ですが、コメントに記載されているように、他のコンストラクタのオーバーロードを引き起こす可能性があります。

私はあなたがそれを期待するようILがあるとして、それは、CLRでの最適化だ確信している - と異なるメソッドにコンストラクタ呼び出しを移動すると、いずれかのものは変更されません:

using System; 

public class Test 
{ 
    static void Main() 
    { 
     // Declaring as object to avoid using the == overload in string 
     object x = CreateString(new char[0]); 
     object y = CreateString(new char[0]); 
     object z = CreateString(new char[1]); 
     Console.WriteLine(x == y); // True 
     Console.WriteLine(x == z); // False   
    } 

    static string CreateString(char[] c) 
    { 
     return new string(c); 
    } 
} 

今CLRがオープンソースであることを確認するには、これがどこで実行されているかを調べることができます。それはobject.cppにあるように見えます - 出現をGetEmptyStringで検索すると、長さ0の文字列が構築されているさまざまなケースで使用されています。

+0

なぜあなたは最適化と言うのですか? –

+2

新しい文字列を計算する必要がないので、これは定数@KyloRenになりました –

+1

私が知っている唯一の例は 'string'ですが、その単一のコンストラクタに限定されていません:' object.ReferenceEquals ( ''、0)、新しい文字列(new char [0])) 'も' true'を返します。 – hvd

関連する問題