Why would you use String.Equals over ==? [duplicate]
Asked Answered
C

8

416

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?

Cleocleobulus answered 2/11, 2009 at 1:55 Comment(2)
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#.Damondamour
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)Slim
C
415

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.

Claque answered 2/11, 2009 at 1:58 Comment(23)
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.Claque
Functionality wise they are the same, but computationally there are slight differences.Cornu
@Yuriy Perhaps you could elaborate or provide a link on the differences?Thibault
From Blaenk's answer: dotnetperls.com/string-equals-compareClaque
Basically, it boils down to one less null check (since you don't have to check whether this is null or not)Claque
Isn't string.Equals considered a MSFT best practice (via FxCop?)Gobo
There is a big difference. If one of the strings are null, then .Equals will throw an exception.Paleobiology
@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.Claque
@Matthew: Exactly my point, why risk a NullReferenceException? I would always use a == b instead of a.Equals(b). Am I off on this?Paleobiology
@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.Claque
The link above doesn't work any longer, but this one does: dotnetperls.com/string-equalsWretched
@MatthewScharley There is practical difference between string.Equals and ==Nachison
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.Methodical
@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 objects 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.Claque
It is best to avoid using the == and != operators when you compare strings.Linhliniment
@Paleobiology doesn't the string.== operator does the same thing internally as string.Equals?Leid
@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.Godinez
@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.Claque
@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.Godinez
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.Godinez
@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.Claque
@Paleobiology 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().Lumpy
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.Bothy
N
137

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
Nachison answered 26/2, 2013 at 13:37 Comment(7)
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.Telescope
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, whereas the latter, as a run-time value, is not.Cordwain
Why do you use camelCase string for Equals but CapCase String for ReferenceEquals?Incommunicable
@AlexMcMillan, I could use string instead String as string is just alias of System.StringNachison
@Nachison that's my point.. I was wondering if you had a specific reason, or if it was just an inconsistency.Incommunicable
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.Handhold
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.Mieshamiett
V
101

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));
Valentinvalentina answered 30/9, 2013 at 19:51 Comment(6)
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.Allochthonous
@AndrewArnott If I execute "a" == "ab".Substring(0, 1) on csharppad.com I get back true. Same if I use String objects. Explanation?Soredium
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.Valentinvalentina
@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.Anaesthesiology
@Anaesthesiology 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.Valentinvalentina
@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.Diarmit
A
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.

Antibody answered 2/11, 2009 at 2:1 Comment(2)
This should get much higher, as culture-aware (and explicit case sensitivity) comparisons are better practive - e.g. learn.microsoft.com/en-us/dotnet/standard/base-types/…Bigler
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.Lumpy
R
37

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");
Resurrection answered 12/4, 2012 at 11:35 Comment(0)
P
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."

Pentameter answered 2/11, 2009 at 1:58 Comment(2)
What exactly qualifies as a "short" string?Unseasoned
@AndHeCodedIt Depends on the speed of your system I presume. Run a benchmark.Irrigate
T
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.

Thuythuya answered 15/10, 2012 at 12:57 Comment(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 integersHolcombe
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.Allochthonous
C
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
Crenate answered 12/10, 2012 at 14:8 Comment(1)
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.Hurwitz

© 2022 - 2024 — McMap. All rights reserved.