2017-07-07 103 views
15

我試着這個代碼示例和OpTestSystem.Console.WriteLine(s == t);它返回false。有人可以解釋這一點嗎?StringBuilder和字符串相等性檢查

public static void OpTest<T>(T s, T t) where T : class 
{ 
    System.Console.WriteLine(s == t); 
} 
static void Main() 
{ 
    string s1 = "строка"; 
    System.Text.StringBuilder sb = new System.Text.StringBuilder(s1); 
    System.Console.Write(sb); 
    string s2 = sb.ToString(); 
    OpTest<string>(s1, s2); 
} 
+0

它有助於查看IL例如使用ildasm自己研究這種行爲。 – Jeroen

+0

如果您想在泛型代碼中使用「EqualityComparer .Default.Equals(s,t)」進行比較,並且可以選擇允許用戶通過他們自己的「IEqualityComparer 」。 – CodesInChaos

+0

[C#==和Equals()之間的區別可能重複](https://stackoverflow.com/questions/814878/c-sharp-difference-between-and-equals) – Dukeling

回答

16

你的泛型方法基本被執行參考平等檢查 - 和s1的價值觀和s2指不同但平等的字符串。你可以這樣更容易證明這一點:

string x = "test"; 
string y = new string(x.ToCharArray()); 
Console.WriteLine(x == y); // Use string overload, checks for equality, result = true 
Console.WriteLine(x.Equals(y)); // Use overridden Equals method, result = true 
Console.WriteLine(ReferenceEquals(x, y)); // False because they're different objects 
Console.WriteLine((object) x == (object) y); // Reference comparison again - result = false 

請注意,您在OpTest約束不改變其==運營商使用。這是在編譯時根據T的限制確定的。請注意,運營商絕不會覆蓋,只有超載。這意味着實現是在編譯時選擇的,而不管執行時的類型如何。

如果您約束T以導致==運算符過載的某種類型,那麼編譯器將使用該過載。例如:

using System; 

class SillyClass 
{ 
    public static string operator ==(SillyClass x, SillyClass y) => "equal"; 
    public static string operator !=(SillyClass x, SillyClass y) => "not equal"; 
} 

class SillySubclass : SillyClass 
{ 
    public static string operator ==(SillySubclass x, SillySubclass y) => "sillier"; 
    public static string operator !=(SillySubclass x, SillySubclass y) => "very silly"; 
} 

class Test 
{ 
    static void Main() 
    { 
     var x = new SillySubclass(); 
     var y = new SillySubclass(); 
     OpTest(x, y); 
    } 

    static void OpTest<T>(T x, T y) where T : SillyClass 
    { 
     Console.WriteLine(x == y); 
     Console.WriteLine(x != y); 
    } 
} 

這裏OpTest方法使用重載運營商 - 但永遠只能從SillyClass,不SillySubclass的人。

2

s == t in OpTest<T>方法檢查引用相等,而不是值相等。在這種情況下,由於類別StringBuilder的參考源的差異,返回false

要獲得true值,則需要使用Equals方法:

public static void OpTest<T>(T s, T t) where T : class 
{ 
    System.Console.WriteLine(s.Equals(t)); 
} 

演示:.NET Fiddle Example

+0

注意:如果's'是null:'string.Equals(s,t)'。 – JohnLBevan

2

這是因爲您使用的是通用的方法,並明確限制了一般的參數類型的class

默認情況下,泛型類型沒有定義相等運算符==

將類別<T>的可能類型限制爲課程使得使用s == t成爲可能。但是,現在它將使用由class限制指定的默認實現,並且使用引用相等。

由於你的一個字符串來自StringBuilder,它將創建一個新的引用,儘管該字符串的內容是相同的。

如果在兩種情況下使用相同的字符串文字,它將返回true,因爲文字只生成一次,然後每次使用時都會引用該文字。

4

已經有很多答案,但我有額外的東西要添加。如果您遇到這種問題,可以使用ildasm.exe來查看生成的IL。例如:

public class Foo 
{ 
    public static void OpTest_1<T>(T s, T t) where T : class 
    { 
     var val = s == t; 
    } 

    public static void OpTest_2(string s, string t) 
    { 
     var val = s == t; 
    } 

    // Does not compile. 
    //public static void OpTest_3<T>(T s, T t) where T : struct 
    //{ 
    // var val = s == t; 
    //} 
} 

給出了OpTest_1

.method public hidebysig static void OpTest_1<class T>(!!T s, !!T t) cil managed 
{ 
    // Code size  17 (0x11) 
    .maxstack 2 
    .locals init ([0] bool val) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: box  !!T 
    IL_0007: ldarg.1 
    IL_0008: box  !!T 
    IL_000d: ceq 
    IL_000f: stloc.0 
    IL_0010: ret 
} // end of method Foo::OpTest_1 

所以你看它調用ceq其檢查參考平等。

其他人有此IL:

.method public hidebysig static void OpTest_2(string s, string t) cil managed 
{ 
    // Code size  10 (0xa) 
    .maxstack 2 
    .locals init ([0] bool val) 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: call  bool [mscorlib]System.String::op_Equality(string, string) 
    IL_0008: stloc.0 
    IL_0009: ret 
} // end of method Foo::OpTest_2 

不使用ceqmscorlib字符串相等操作,將給出一個結果如預期。

就像我說的,只是爲了增加研究這個問題的另一種方式。對於更高層次的細節,我建議閱讀@JonSkeet's answer

+0

「所以你看它確實包含了兩個字符串」 - 不是真的。這裏的'box'指令在給定約束的情況下有點毫無意義......只有當類型參數是一個值類型時它纔會執行任何操作,它不在這裏。 –

+0

@JonSkeet謝謝你的糾正,我在這裏學習:D。我清楚地刪除了該評論,希望能讓我的回答正確。 (不知道如何將你的評論的第二部分納入我的答案中,但如果你看到了答案並且認爲它增加了答案,可以隨意編輯。) – Jeroen