String.Equals vs String.Compare vs "==" in Action. Explanation needed

11.9k Views Asked by At

Following is the code snippet from a console application -

class MyClass
{        
   public int GetDay(string data22)
    {
        int returnValue = 0;

        if (string.Compare(data22,"THURSDAY") == 0) // true
        {
            returnValue = (int)DayOfWeek.Thursday;
        }

        if (data22 == "THURSDAY") //false
        {
            returnValue = (int)DayOfWeek.Thursday;
        }

        if (string.Equals(data22, "THURSDAY"))//false
        {
            returnValue = (int)DayOfWeek.Thursday;
        }
        return returnValue;
    }
}

class Program
{
    static void Main(string[] args)
    {
        string ExecutionDay = "‎THURSDAY";
        MyClass obj1 = new MyClass();
        int MyDays = obj1.GetDay(ExecutionDay);
    }
}

Question is - Why does the first comparison (string.compare) work and the other two comparison methods does not work in THIS PARTICULAR CASE ?

4

There are 4 best solutions below

6
On BEST ANSWER

Why does the first comparison (string.compare) work and the other two comparison methods does not work in THIS PARTICULAR CASE

There are invisible characters (particularly, a Left-to-Right mark (Thanks @MatthewWatson)) in your code. You can view them with any hex editor:

enter image description here

This is over-looked by string.Compare, while it isn't with string.Equals. You can see it in the docs:

Notes to Callers:

Character sets include ignorable characters. The Compare(String, String) method does not consider such characters when it performs a culture-sensitive comparison. For example, if the following code is run on the .NET Framework 4 or later, a culture-sensitive comparison of "animal" with "ani-mal" (using a soft hyphen, or U+00AD) indicates that the two strings are equivalent.

0
On

The ExecutionDay string contains invisible characters, otherwise all checks would be true

The following lines return a different length, 9 and 8 respectively

        Console.WriteLine(ExecutionDay.Length);
        Console.WriteLine("THURSDAY".Length);
2
On

There is an "invisible" character at the beginning of your

string ExecutionDay = "‎THURSDAY";

and it is the LEFT-TO-RIGHT MARK. You can check it with:

int len = ExecutionDay.Length; // 9 instead of 8

and

char ch = ExecutionDay[0]; // 8206
0
On

In short CompareTo is culture dependent. For example ß (s sharp from German):

Console.WriteLine("ß Compare ss 1: " + ("ß".CompareTo("ss") == 0));
Console.WriteLine("ß Compare ss 2: " + (String.Compare("ß", "ss", StringComparison.Ordinal) == 0));
Console.WriteLine("ß equals ss: " + "ß".Equals("ss"));
Console.WriteLine("ß == ss: " + ("ß" == "ss"));

Would print out

ß Compare ss 1: True
ß Compare ss 2: False
ß equals ss: False
ß == ss: False

In your case you have string that look the same, but are different. Usally I find it helpful to see the difference, this can be done with:

Console.WriteLine(
  string.Join(", ", 
    ExecutionDay
      .ToCharArray()
      .Select(o =((int)o).ToString(CultureInfo.InvariantCulture))
      .AsEnumerable()
  )
);

resulting in character number code list:

8206, 84, 72, 85, 82, 83, 68, 65, 89

Where character 8206 in unicode is left-to-right mark.


== comparison

This will compare values ONLY IF it is a value type or primitive. Other then that it is and should be used as REFERENCE comparison. In C#, type string is special and both of 2 following assertions would yield to true:

string ss = getinput();//"SS"
assertTrue("SS"=="SS");
assertTrue( ss =="SS");

In Java however first will return true beacuse JIT runtime optimizer that collects all unique strings from code and creates a table that is used. Second one false, as string is just immutable character array and if entered by user a new memory space is used thus reference comparison returns false.

C# Guidelines for Implementing Equals and the Equality Operator (==)

Equals comparison

Unlike == Equals method is just a virtual one defined in System.Object, and overridden by whichever classes choose to do so. Therefor the overridden version will be used and in case of string type this means contents comparison will be done.

Follow these guidelines when overriding Equals(Object):

  • Types that implement IComparable must override Equals(Object).
  • Types that override Equals(Object) must also override GetHashCode; otherwise, hash tables might not work correctly.
  • You should consider implementing the IEquatable interface to support strongly typed tests for equality. Your IEquatable.Equals implementation should return results that are consistent with Equals.
  • If your programming language supports operator overloading and you overload the equality operator for a given type, you must also override the Equals(Object) method to return the same result as the equality operator. This helps ensure that class library code that uses Equals (such as ArrayList and Hashtable) behaves in a manner that is consistent with the way the equality operator is used by applicat

C# Implementing the Equals Method

ComparedTo comparison

Caution

The CompareTo method was designed primarily for use in sorting or alphabetizing operations. It should not be used when the primary purpose of the method call is to determine whether two strings are equivalent. To determine whether two strings are equivalent, call the Equals method.

This method performs a word (case-sensitive and culture-sensitive) comparison using the current culture. For more information about word, string, and ordinal sorts, see System.Globalization.CompareOptions.

Source : Manual