StringBuilder和字符串相等性检查

时间:2017-07-07 07:18:32

标签: c#

我正在尝试此代码示例,并在1200px返回OpTestSystem.Console.WriteLine(s == t);。有人可以解释一下吗?

false

4 个答案:

答案 0 :(得分:16)

您的泛型方法基本上将执行引用相等性检查 - s1s2的值引用不同但相等的字符串。你可以像这样更容易地展示:

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

答案 1 :(得分: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

那不使用ceq,而是使用mscorlib中的字符串相等操作,并按预期给出结果。

就像我说的那样,只是为了增加另一种研究这个问题的方法。有关更高级别的详细信息,建议您阅读@JonSkeet's answer

答案 2 :(得分:2)

s == t方法中的

OpTest<T>检查引用相等性,而不是值相等。在这种情况下,由于false类的参考来源不同,它会返回StringBuilder

要获得true值,您需要使用Equals方法:

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

演示:.NET Fiddle Example

答案 3 :(得分:2)

这是因为您使用的是通用方法,并且您将通用参数专门限制为class的类型。

默认情况下,泛型类型没有定义相等运算符==

<T>的可能类型限制为类可以使s == t成为可能。但是,现在它将使用class限制指定的默认实现,并使用引用相等。

由于你的一个字符串来自StringBuilder,它会创建一个新的引用,尽管字符串的内容是相同的。

如果在两种情况下使用相同的字符串文字,它将返回true,因为文字仅生成一次,然后每次使用时都会引用它。