408

I recently was introduced to a large codebase and noticed all string comparisons are done using String.Equals() instead of ==

What's the reason for this, do you think?

D Stanley
  • 149,601
  • 11
  • 178
  • 240
JamesBrownIsDead
  • 4,573
  • 3
  • 20
  • 10
  • 57
    Re : Duplicate closure - note that this question is specifically for string, whereas the accepted answer in the referenced duplicate refers to the general case of object. This is significantly different in .Net vs Java as `==` can be used to compare string contents in C#. – StuartLC Feb 26 '14 at 07:22
  • 3
    `Equals` may be preferred since the appearance of the _null-propagation operator_, because you may return `false` also in case of both string `null`, in a very concise way; like this: `if(firstString?.Equals(secondString) ?? false)` – Teodor Tite Feb 10 '17 at 09:57

8 Answers8

407

It's entirely likely that a large portion of the developer base comes from a Java background where using == to compare strings is wrong and doesn't work.

In C# there's no (practical) difference (for strings) as long as they are typed as string.

If they are typed as object or T then see other answers here that talk about generic methods or operator overloading as there you definitely want to use the Equals method.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • 9
    Not as far as the values returned goes. `.Equals` does offer more options but `str1.Equals(str2)` is the same as `str1 == str2` as far as what you get as a result. I do believe they use different methods for determining the equality as Jon Skeets quote that Blaenk brings up would indicate, but they give the same values. – Matthew Scharley Nov 02 '09 at 02:04
  • 1
    Functionality wise they are the same, but computationally there are slight differences. – Yuriy Faktorovich Nov 02 '09 at 02:32
  • 2
    @Yuriy Perhaps you could elaborate or provide a link on the differences? – Kirk Broadhurst Nov 02 '09 at 06:57
  • 1
    From Blaenk's answer: http://dotnetperls.com/string-equals-compare – Matthew Scharley Nov 02 '09 at 07:13
  • 1
    Basically, it boils down to one less null check (since you don't have to check whether `this` is null or not) – Matthew Scharley Nov 02 '09 at 07:20
  • Isn't string.Equals considered a MSFT best practice (via FxCop?) – Robaticus Sep 09 '10 at 14:26
  • 126
    There is a big difference. If one of the strings are null, then .Equals will throw an exception. – Mas Oct 05 '11 at 15:41
  • 43
    @Mas: The only exception ever thrown by `.Equals` is a null reference exception if you try to call it on an object that is null, ie. `string str1 = null; str1.Equals()`. This isn't an issue with `Equals()`, that happens with any function and should be self-evident. The actual functionality of the two comparisons is always exactly the same given the same inputs. – Matthew Scharley Oct 05 '11 at 21:10
  • 1
    @Matthew: Exactly my point, why risk a NullReferenceException? I would always use `a == b` instead of `a.Equals(b)`. Am I off on this? – Mas Oct 05 '11 at 21:39
  • @Mas: In C#, you'd be totally spot on. But as I said in my answer, people from other backgrounds may be used to other things because the language they are most familiar with doesn't work quite the same way. – Matthew Scharley Oct 06 '11 at 02:51
  • 7
    The link above doesn't work any longer, but this one does: http://www.dotnetperls.com/string-equals – ThomasW Feb 22 '12 at 08:36
  • @MatthewScharley There is practical difference between `string.Equals and ==` – vikas Feb 26 '13 at 13:38
  • 1
    I came across an example in the last 5 mins where == and string.Equals do NOT produce the same result. The equality check evaluated false with == and true with string.Equals. Both strings are string properties of an object created from reading an XML file (using System.Xml.Serialization.XmlSerializer). They are identical in the XML. I don't currently have an explanation, but thought it worth mentioning. I guess it's some kind of reference issue. – Nande Apr 06 '14 at 04:28
  • 1
    @Nande, Sounds like Xamarin/mono is buggy then. In .NET, String == String is supposed to use value equality. Note, this *only* works if both instances are instances of `string`! `(object)"a" == (object)"a"` will use `object`s equality operator which will use reference equality and return false in a non-trivial example (in this exact case the compiler will use the same memory for both equivalent compile-time constants). See vikas's answer below. – Matthew Scharley Apr 06 '14 at 05:14
  • [It is best to avoid using the == and != operators when you compare strings.](https://msdn.microsoft.com/en-us/library/cc165449.aspx) – shmosel Feb 27 '17 at 20:33
  • @Mas doesn't the `string.==` operator does the same thing internally as `string.Equals`? – mr5 May 25 '17 at 01:36
  • @Matthew "why risk a NullPointerException?" - Well, because often, if I'm at the point of comparing Strings, I'm going to use one or both of them and expect neither to be null, and I want to know as soon as possible if one is null so as not to continue normal execution under undefined conditions. The NullPointerException wasn't created to be evil, it exists as a safeguard for that situation. There are certainly situations where you don't care if a String is null, but I don't think it makes much sense to have a blanket conception that whatever approach doesn't throw the exception is best. – Tim M. Feb 13 '18 at 21:18
  • @tradeJmark if you are comparing two 'string's where one of them is null for equality then I don't want to wrap my comparison in a try/catch just to handle one of the objects being null. Exceptions aren't evil, they're just damned annoying when all I'm trying to do is a comparison inside an if statement. – Matthew Scharley Feb 16 '18 at 12:16
  • @Matthew Well, sure, like I said, there are situations where you don't care if a String is null. I would have guessed it's much more common for people to deal with empty strings than to allow them to be null during normal program operation, but that can just be a decision of style. I was just trying to give an answer to why one would "risk" a NullPointerException, I didn't mean it always has to be done my way. – Tim M. Feb 16 '18 at 14:36
  • I guess I would tend to specifically make my program use empty Strings for normal input and intentionally reserve null Strings for errors, just so I can always tell what's good data and what's bad, but that doesn't have to be how you do it, there are other ways. – Tim M. Feb 16 '18 at 14:40
  • @tradeJmark you still risk missing a null string anyway - the one you pass in as the argument to `Equals()`. Don't rely on NullPointerException's to catch your nulls - you should be checking if you suspect a null is possible. – Matthew Scharley Mar 09 '18 at 22:51
  • 1
    @Mas It will only throw an exception if the string on which you call `.Equals()` is null. This is why you should/could use `string.Equals()` instead of `myString.Equals()`. – Caltor May 04 '20 at 12:57
  • Java string '==' is "broken" in the exact same way the .NET string 'ReferenceEquals' is "broken". Basically, for any language that uses string interning (most, I think), you simply can never rely on string identity equality to be useful or meaningful. – Dave Doknjas Jun 30 '21 at 15:36
135

There is practical difference between string.Equals and ==

bool result = false;

object obj = "String";    
string str2 = "String";
string str3 = typeof(string).Name;
string str4 = "String";
object obj2 = str3;

// Comparision between object obj and string str2 -- Com 1
result = string.Equals(obj, str2);// true
result = String.ReferenceEquals(obj, str2); // true
result = (obj == str2);// true

// Comparision between object obj and string str3 -- Com 2
result = string.Equals(obj, str3);// true
result = String.ReferenceEquals(obj, str3); // false
result = (obj == str3);// false

// Comparision between object obj and string str4 -- Com 3
result = string.Equals(obj, str4);// true
result = String.ReferenceEquals(obj, str4); // true
result = (obj == str4);// true

// Comparision between string str2 and string str3 -- Com 4
result = string.Equals(str2, str3);// true
result = String.ReferenceEquals(str2, str3); // false
result = (str2 == str3);// true

// Comparision between string str2 and string str4 -- Com 5
result = string.Equals(str2, str4);// true
result = String.ReferenceEquals(str2, str4); // true
result = (str2 == str4);// true

// Comparision between string str3 and string str4 -- Com 6
result = string.Equals(str3, str4);// true
result = String.ReferenceEquals(str3, str4); // false
result = (str3 == str4);// true

// Comparision between object obj and object obj2 -- Com 7
result = String.Equals(obj, obj2);// true
result = String.ReferenceEquals(obj, obj2); // false
result = (obj == obj2);// false

Adding Watch

obj     "String" {1#}   object {string}
str2    "String" {1#}   string
str3    "String" {5#}   string
str4    "String" {1#}   string
obj2    "String" {5#}   object {string}

Now look at {1#} and {5#}

obj, str2, str4 and obj2 references are same.

obj and obj2 are object type and others are string type

Conclusion:

  1. com1: result = (obj == str2);// true
    • compares object and string so performs a reference equality check
    • obj and str2 point to the same reference so the result is true
  2. com2: result = (obj == str3);// false
    • compares object and string so performs a reference equality check
    • obj and str3 point to the different references so the result is false
  3. com3: result = (obj == str4);// true
    • compares object and string so performs a reference equality check
    • obj and str4 point to the same reference so the result is true
  4. com4: result = (str2 == str3);// true
    • compares string and string so performs a string value check
    • str2 and str3 are both "String" so the result is true
  5. com5: result = (str2 == str4);// true
    • compares string and string so performs a string value check
    • str2 and str4 are both "String" so the result is true
  6. com6: result = (str3 == str4);// true
    • compares string and string so performs a string value check
    • str3 and str4 are both "String" so the result is true
  7. com7: result = (obj == obj2);// false  - compares object and object so performs a reference equality check      - obj and obj2 point to the different references so the result is false
Community
  • 1
  • 1
vikas
  • 2,780
  • 4
  • 27
  • 37
  • 32
    Simple explanation for all cases above: `string.Equals(object, object)` **is** `object.Equals(object, object)` which calls `Equals` on the first argument, unlike `==` operator, which, unless overloaded, calls `ReferenceEquals`. – Athari May 21 '13 at 15:49
  • 14
    I believe that the reason that `"String"` and `typeof(string).Name` are different references is that the former, as a compile-time constant, is [interned](http://msdn.microsoft.com/en-us/library/system.string.intern.aspx), whereas the latter, as a run-time value, is not. – Carl G Jun 25 '13 at 14:07
  • Why do you use camelCase `string` for `Equals` but CapCase `String` for `ReferenceEquals`? – Alex McMillan Sep 05 '16 at 20:31
  • @AlexMcMillan, I could use `string` instead `String` as `string` is just alias of `System.String` – vikas Oct 14 '16 at 07:19
  • @vikas that's my point.. I was wondering if you had a specific reason, or if it was just an inconsistency. – Alex McMillan Oct 15 '16 at 06:26
  • 2
    re: "ComX". Back in the bad old days of DOS, I received some data in which someone had named the file "com3" ("Com" for "community" in this case, not "comparison")..Of course when trying to access the file, the OS thought it was a com port, with interesting results. 30 years later, but I refrain from naming ''anything'' "com-whatever" unless it really is a com port. – MickeyfAgain_BeforeExitOfSO Jun 27 '17 at 15:30
  • In the case where you are comparing an `object` to a `string`, I'm not sure it's a practical comparison, since if you already know the value is a string in order to consider `string.Equals()` an alternative, you should be using a variable of the appropriate type. You now also have the option of using `obj is "String"`, but not `obj is obj2`, however if you can't narrow down the type from `object`, there's really no point in testing for equality unless you want to use serialization or maybe exhaustive type tests. – brianary Jun 12 '23 at 15:11
99

There is one subtle but very important difference between == and the String.Equals methods:

class Program
{
    static void Main(string[] args)
    {
        CheckEquality("a", "a");
        Console.WriteLine("----------");
        CheckEquality("a", "ba".Substring(1));
    }

    static void CheckEquality<T>(T value1, T value2) where T : class
    {
        Console.WriteLine("value1: {0}", value1);
        Console.WriteLine("value2: {0}", value2);

        Console.WriteLine("value1 == value2:      {0}", value1 == value2);
        Console.WriteLine("value1.Equals(value2): {0}", value1.Equals(value2));

        if (typeof(T).IsEquivalentTo(typeof(string)))
        {
            string string1 = (string)(object)value1;
            string string2 = (string)(object)value2;
            Console.WriteLine("string1 == string2:    {0}", string1 == string2);
        }
    }
}

Produces this output:

value1: a
value2: a
value1 == value2:      True
value1.Equals(value2): True
string1 == string2:    True
----------
value1: a
value2: a
value1 == value2:      False
value1.Equals(value2): True
string1 == string2:    True

You can see that the == operator is returning false to two obviously equal strings. Why? Because the == operator in use in the generic method is resolved to be the op_equal method as defined by System.Object (the only guarantee of T the method has at compile time), which means that it's reference equality instead of value equality.

When you have two values typed as System.String explicitly, then == has a value-equality semantic because the compiler resolves the == to System.String.op_equal instead of System.Object.op_equal.

So to play it safe, I almost always use String.Equals instead to that I always get the value equality semantics I want.

And to avoid NullReferenceExceptions if one of the values is null, I always use the static String.Equals method:

bool true = String.Equals("a", "ba".Substring(1));
Andrew Arnott
  • 80,040
  • 26
  • 132
  • 171
  • 1
    A related gotcha occurs with code that returns things of type `Object`. If `var myItem=GetItem()` reads an `Object` from the database, the expressions `"Hi".Equals(myItem)`, `myItem.Equals("Hi")`, and `Object.Equals("Hi", myItem)` will all return `True` if the returned object is a `string` with two characters "Hi", but `myItem == "Hi"` or `"Hi" == myItem` will test reference equality. The "Option Strict On" dialect of VB.NET is better in that regard. Its "=" operator tests either tests value equality or won't compile; for a reference-equality check, one uses the "Is" operator. – supercat Sep 30 '13 at 22:53
  • 1
    @AndrewArnott If I execute `"a" == "ab".Substring(0, 1)` on http://csharppad.com I get back `true`. Same if I use `String` objects. Explanation? – Krumelur Jun 24 '15 at 08:53
  • 2
    Because in your example, the compiler knows to invoke the `System.String` overload of the `==` operator. The case you have to be cautious of is when the compiler *doesn't* know what type it is, such as when the compared expressions are typed as `T` or `System.Object`. – Andrew Arnott Jun 24 '15 at 16:12
  • 2
    @AndrewArnott Could you elaborate on why `.Substring()` causes different behavior? As it returns a `string`, not a `string` wrapped in an `object`, why does it not have the same behavior as the first check? To me, it seems like both should have the exact same output. It's guaranteed to be a string comparison in the second example, just as it is in the first. – Rob Jul 23 '15 at 02:02
  • 3
    @Rob The comparison is done in a generic method that does *not* know when it is compiled that it will be doing a string comparison. Therefore the `==` operator used in the generic method is an `object ==` operator rather than a `string==` operator. The reason `.Substring(1)` produces a different result is that the method creates a *new* string object rather than possibly reusing another one that already has the same value. Thus as the two strings with equivalent values but different objects are seen as different by the generic method. – Andrew Arnott Jul 26 '15 at 01:23
  • @AndrewArnott I think the main reason is that,It is about string interning.At the first call to CheckEquality("a", "a"); "a" and "a" compile-time constant, is interned, whereas the latter, as a run-time value, is not. – erhan355 Jul 06 '20 at 14:26
50

String.Equals does offer overloads to handle casing and culture-aware comparison. If your code doesn't make use of these, the devs may just be used to Java, where (as Matthew says), you must use the .Equals method to do content comparisons.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
  • 3
    This should get much higher, as culture-aware (and explicit case sensitivity) comparisons are better practive - e.g. https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#recommendations_for_string_usage – Bartosz May 11 '19 at 20:01
  • Agree with @Bartosz! Making your intent clear is Microsoft best practice.Using `==` does not explicitly tell the reader what is expected to happen with regard to case-sensitivity and culture. – Caltor May 04 '20 at 13:19
36

Both methods do the same functionally - they compare values.
As is written on MSDN:

But if one of your string instances is null, these methods are working differently:

string x = null;
string y = "qq";
if (x == y) // returns false
    MessageBox.Show("true");
else
    MessageBox.Show("false");

if (x.Equals(y)) // returns System.NullReferenceException: Object reference not set to an instance of an object. - because x is null !!!
    MessageBox.Show("true");
else
    MessageBox.Show("false");
silkfire
  • 24,585
  • 15
  • 82
  • 105
thezar
  • 1,278
  • 13
  • 17
17

There's a writeup on this article which you might find to be interesting, with some quotes from Jon Skeet. It seems like the use is pretty much the same.

Jon Skeet states that the performance of instance Equals "is slightly better when the strings are short—as the strings increase in length, that difference becomes completely insignificant."

Tharwen
  • 3,057
  • 2
  • 24
  • 36
Jorge Israel Peña
  • 36,800
  • 16
  • 93
  • 123
8

I want to add that there is another difference. It is related to what Andrew posts.

It is also related to a VERY annoying to find bug in our software. See the following simplified example (I also omitted the null check).

public const int SPECIAL_NUMBER = 213;

public bool IsSpecialNumberEntered(string numberTextBoxTextValue)
{
    return numberTextBoxTextValue.Equals(SPECIAL_NUMBER)
}

This will compile and always return false. While the following will give a compile error:

public const int SPECIAL_NUMBER = 213;

public bool IsSpecialNumberEntered(string numberTextBoxTextValue)
{
    return (numberTextBoxTextValue == SPECIAL_NUMBER);
}

We have had to solve a similar problem where someone compared enums of different type using Equals. You are going to read over this MANY times before realising it is the cause of the bug. Especially if the definition of SPECIAL_NUMBER is not near the problem area.

This is why I am really against the use of Equals in situations where is it not necessary. You lose a little bit of type-safety.

Matthijs Wessels
  • 6,530
  • 8
  • 60
  • 103
  • 2
    that is why it is good to use the static string.Equals method always with the StringComparison argument - that overload will produce a compile error when trying to compare strings and integers – Marek Nov 23 '12 at 16:44
  • I wish method and operator parameters could have attributes (and that `Equals` and `ReferenceEquals` had always used them) that would disallow implicit conversions, upcasts, boxing conversions, or combinations of the above. I also wish some operator other than `==` had been used for reference equality. Having `Equals` and `==` define equivalence relations with all argument types where they compile would be more helpful than having them compile in cases that do not yield equivalence relations. – supercat Sep 27 '13 at 19:06
6

I've just been banging my head against a wall trying to solve a bug because I read this page and concluded there was no meaningful difference when in practice there is so I'll post this link here in case anyone else finds they get different results out of == and equals.

Object == equality fails, but .Equals succeeds. Does this make sense?

string a = "x";
string b = new String(new []{'x'});

Console.WriteLine("x == x " + (a == b));//True
Console.WriteLine("object x == x " + ((object)a == (object)b));//False
Console.WriteLine("x equals x " + (a.Equals(b)));//True
Console.WriteLine("object x equals x " + (((object)a).Equals((object)b)));//True
Community
  • 1
  • 1
Andrew
  • 547
  • 4
  • 13
  • As I understand it, the second equality check fails as `Object == Object` resolves to `System.Object.ReferenceEquals` and `a` and `b` are two different objects in memory. The remainder compare the values of the string objects. Even the last comparison, which seems to call `.Equals` for the `Object` type object, as it is implemented as a `virtual` method, it defaults to calling `String.Equals`. – Lovethenakedgun Mar 30 '20 at 07:30