Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StringBuilder and string equality check

Tags:

c#

I am trying this sample of code and OpTest when System.Console.WriteLine(s == t); it returns false. Can somebody explain this?

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);
}
like image 492
Максим Кадров Avatar asked Jul 07 '17 07:07

Максим Кадров


People also ask

How do you check for equality in StringBuilder?

The Equals method uses ordinal comparison to determine whether the strings are equal. . NET Core 3.0 and later versions: The current instance and sb are equal if the strings assigned to both StringBuilder objects are the same. To determine equality, the Equals method uses ordinal comparison.

Can I compare String and StringBuilder?

We can use the equals() method for comparing two strings in Java since the String class overrides the equals() method of the Object class, while StringBuilder doesn't override the equals() method of the Object class and hence equals() method cannot be used to compare two StringBuilder objects.

How do you know if two String builders are equal?

Syntax: public bool Equals (System. Text. StringBuilder sb); Here, sb is an object to compare with this instance, or null. Return Value: It will return true if this instance and sb have an equal string, Capacity, and MaxCapacity values; otherwise, false.

Are String and StringBuilder identical types?

StringBuilder and String are completely different objects.


2 Answers

Your generic method will basically be performing a reference equality check - and the values of s1 and s2 refer to different but equal strings. You can show this more easily like this:

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

Note that your constraint in OpTest doesn't change which == operator is used. That's determined at compile-time, based on the constraints on T. Note that operators are never overridden, only overloaded. That means the implementation is chosen at compile-time, regardless of the type at execution time.

If you constrained T to derive from some type which overloads the == operator, then the compiler will use that overload. For example:

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);
    }
}

Here the OpTest method does use the overloaded operators - but only ever the ones from SillyClass, not SillySubclass.

like image 183
Jon Skeet Avatar answered Sep 28 '22 10:09

Jon Skeet


There are many answers already, but I have something extra to add. If you're stuck on this kind of issue it can help to use ildasm.exe to look at the generated IL. For example:

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;
    //}
}

Gives for 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

So you see it calls ceq which checks for reference equality.

The other one has this 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

That doesn't use ceq but the string equality operation in mscorlib and will give a result as expected.

Like I said, just to add another way of researching this issue. For more high level details I'd recommend reading @JonSkeet's answer.

like image 22
Jeroen Avatar answered Sep 28 '22 11:09

Jeroen