Search code examples
c#stringcomparestring-comparison

what should be the return value of StringComparison.OrdinalIgnoreCase?


When i execute the below line

returnVal=string.Compare("stringOne","stringTwo",StringComparison.OrdinalIgnoreCase);

I get the returnVal as -5. Can anyone please explain me why is it like that? And also with other string values i get return value as 13,15 etc. It should usually be -1,0,1. If i am not wrong.


Solution

  • "It should usually be -1,0,1. If i am not wrong"

    You are correct that those are typically the return values of the Compare method, and the recommended practice, even in the documentation for IComparer.Compare:

    https://learn.microsoft.com/en-us/troubleshoot/dotnet/csharp/use-icomparable-icomparer

    "The IComparer.Compare method requires a tertiary comparison. 1, 0, or -1 is returned depending on whether one value is greater than, equal to, or less than the other. The sort order (ascending or descending) can be changed by switching the logical operators in this method."

    However, this isn't enforced anywhere.

    Also, the String class doesn't implement IComparer<string>, and this overload of the Compare signature is not defined in that interface anyway.

    If we look at the source code for the Compare method here, we see that for OrdinalIgnoreCase, it calls the CompareOrdinalIgnoreCaseHelper method, which returns the ascii value difference between the first pair of non-matching characters (after upper-casing them). If the strings are different lengths but the first characters of the longer one all match those of the shorter, then the difference in their length is returned.

    private unsafe static int CompareOrdinalIgnoreCaseHelper(String strA, String strB)
    {
        Contract.Requires(strA != null);
        Contract.Requires(strB != null);
        Contract.EndContractBlock();
        int length = Math.Min(strA.Length, strB.Length);
    
        fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
        {
            char* a = ap;
            char* b = bp;
    
            while (length != 0) 
            {
                int charA = *a;
                int charB = *b;
    
                Contract.Assert((charA | charB) <= 0x7F, "strings have to be ASCII");
    
                // uppercase both chars - notice that we need just one compare per char
                if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
                if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
    
                //Return the (case-insensitive) difference between them.
                if (charA != charB)
                    return charA - charB;
    
                // Next char
                a++; b++;
                length--;
            }
    
            return strA.Length - strB.Length;
        }
    }
    

    Therefore, we should always compare the result of a Compare method to 0 and not -1 or 1.