8
        string s1 = "t";
        string s2 = 't'.ToString();        

        Console.WriteLine(s1.Equals(s2)); // returning true
        Console.WriteLine(object.Equals(s1, s2)); // returning true

Here it is returning same result. Now when I'm using StringBuilder it is not returning same value. What is the underneath reason?

        StringBuilder s1 = new StringBuilder();
        StringBuilder s2 = new StringBuilder();

        Console.WriteLine(s1.Equals(s2)); // returning true
        Console.WriteLine(object.Equals(s1, s2)); // returning false

Edit1: My above question answered below. But during this discussion what we find out StringBuilder doesn't have any override Equals method in its implementation. So when we call StringBuilder.Equals it actually goes to Object.Equals. So if someone calls StringBuilder.Equals and S1.Equals(S2) the result will be different.

Pritam Karmakar
  • 2,773
  • 5
  • 30
  • 49
  • 1
    Good catch! Bottom line, `StringBuilder` seems to forget to override `Equals(object)`. It seems counter-intuitive to have `Equals(StringBuilder)` having different behavior from `Equals(object)`. – leppie Mar 19 '12 at 06:02

3 Answers3

7

String.Equals() is overriden in C# such that identical strings are in fact Equal() when the Equal() override defined on string is used.

If you are comparing string literals (not the case in your example), it's worth noting that identical string literals are interned... that is, identical strings live at the same address so will also be equal by reference (e.g. object.Equals() or s1.ReferenceEquals(s2)) as well as by value.

StringBuilder provides an overload to Equals() that takes StringBuilder as a parameter (that is s1.Equals(s2) will call that overload instead of calling object.Equals(object obj)).

http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.equals.aspx

StringBuilder.Equals() is...

true if this instance and sb have equal string, Capacity, and MaxCapacity values; otherwise, false.

object.Equals() uses the static Equals() defined on object, which checks only for reference equality (if passed a class) or for value equality (if passed a struct).

So in summary

string s1 = "t";
string s2 = 't'.ToString();        

Console.WriteLine(s1.Equals(s2)); // true because both reference equality (interned strings) and value equality (string overrides Equals())
Console.WriteLine(object.Equals(s1, s2)); // true because of reference equality (interned strings)

StringBuilder s1 = new StringBuilder();
StringBuilder s2 = new StringBuilder();

Console.WriteLine(s1.Equals(s2)); // true because StringBuilder.Equals() overloaded
Console.WriteLine(object.Equals(s1, s2)); // false because the two StringBuilder instances have different addresses (references not equal)
Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • 5
    Technically StringBuilder only overloads Equals method without overriding virtual object.Equals(object). This is the reason why two methods behave differently. I wonder if they forgot to do it in the NET1.0 and did not want to introduce a breaking change? – alexm Mar 19 '12 at 01:55
  • @alexm: Good catch. I had not noticed that before. Updated my answer. – Eric J. Mar 19 '12 at 01:59
  • Thaks Eric for the nice explanation. If I make a small summary out of it: object.Equals will check for reference or value depend upon the input object type. Whereas String and StringBuilder has their own overload/overridden Equal method that will work as per their implementation. Please correct me if I'm wrong. – Pritam Karmakar Mar 19 '12 at 02:30
  • Also I have updated 2 stringbuilder declarations as - StringBuilder s1 = new StringBuilder("abc", 10); StringBuilder s2 = new StringBuilder("abc", 10); Then why below 2 instances are returning different value Console.WriteLine(s1.Equals(s2)); // true Console.WriteLine(StringBuilder.Equals(s1, s2)); // false Thanks in advance – Pritam Karmakar Mar 19 '12 at 02:38
  • Identical strings are _not_ interned by default; this is easy to prove with the following code: `object.ReferenceEquals(new StringBuilder("abc").ToString(), new StringBuilder("abc").ToString())` (which returns false). Note that identical string literals in a C# program will refer to the same object, though. "When two or more string literals that are equivalent according to the string equality operator (Section 7.9.7) appear in the same assembly, these string literals refer to the same string instance." (http://msdn.microsoft.com/en-us/library/aa691090.aspx) – Bradley Grainger Mar 19 '12 at 03:42
  • @Bradley: I think you have not answered my last question. In above comment what I'm asking: as per msdn (http://msdn.microsoft.com/en-us/library/ccth01t4.aspx) for stringbuilder Equals method "true if this instance and sb have equal string, Capacity, and MaxCapacity values; otherwise, false." To test this I have created 2 sb objects having same content with same capacity. But when I'm using s1.Equals(s2) and StringBuilder.Equals(s1, s2) I'm getting different result. I want to know why it is so. Thx for your reply. – Pritam Karmakar Mar 19 '12 at 04:01
  • @Bradley: Thank you for pointing that out. I have corrected that part of the answer. – Eric J. Mar 19 '12 at 05:52
  • static `object.Equals` should just call the instance `Equals`. Your code examples incorrectly refers to reference equality. You will need `object.ReferenceEquals` for that. – leppie Mar 19 '12 at 05:58
  • @PritamKarmakar: The reason for the behaviour you're seeing is that `StringBuilder` implements `Equals(StringBuilder)` but doesn't override `object.Equals(object)` with the same semantics. I would consider this a bug. @alexm is probably right in guessing that this was a bug in an early version of the Framework, and they haven't fixed it for backwards compatibility. – Bradley Grainger Mar 19 '12 at 10:48
  • @BradleyGrainger: Thanks. Yes looks like it's a bug. I just check StringBuilder implementation and don't see any override Equal method there. – Pritam Karmakar Mar 19 '12 at 17:09
1

The generic Equals method compares the references of two objects to see if they have reference equality for reference types such as StringBuilder. For values types, and string behaves like a value type (is immutable), it does a bitwise comparison (determines if the binary representation is the same). This functionality, however, is overloaded in the StringBuilder class.

According to MSDN, the StringBuilder's equal method will return true if the following criteria for both StringBuilder objects are equal:

  • String
  • Capacity
  • MaxCapacity

Thus, s1 and s2 in the second example fail reference equality, but pass the custom StringBuilder Equals equality based on the criteria just mentioned.

Matt
  • 14,353
  • 5
  • 53
  • 65
  • String is not a value type. (It is a reference type with value semantics.) http://stackoverflow.com/questions/1069155/is-string-a-value-type-or-a-reference-type – Bradley Grainger Mar 19 '12 at 03:43
  • @BradleyGrainger, I knew it isn't technically a value type, but in this case it acts the same. I'll update my answer to clarify though. Thanks. – Matt Mar 19 '12 at 12:22
1

The string class implements Equals in such a way that it compares the values of the strings.

Most object instances, unless they implement a different type of comparison, check to see whether or not the object references are the same.

Note that there is also a case where two different string constants that contain the exact same value are initially assigned to the same object reference by the compiler.

competent_tech
  • 44,465
  • 11
  • 90
  • 113