.Net basics: String comparisons

Let’s start this blog with something simple, but often performed in the wrong way even by experienced .Net developers. I’m talking about string comparisons.

The basic comparison is performed using the == operator (or it’s negation !=), that executes a comparison that is culture insensitive and case sensitive. We should use this operator only when we expect strings to exactly match.
An example is while iterating through an XML document using a XmlReader, we can use the == operator to compare the current node name with the name that we expect. In this case we want the node name to exactly match the string, so using == is the right choice.

XmlTextReader reader = new XmlTextReader(input);
while( reader.Read() )
{
	if( reader.NodeType == XmlNodeType.Element && reader.LocalName == "server" )
	{
		// Do something
	}
}

Using the == operator is the same as using the String.Equals(string, string) static method or the instance method string.Equals(string).

Now let’s have a look at another type of comparison: case insensitive comparison. Too often I see code like

string a = "something";
string b = "Something else";

if( a.ToUpper() == b.ToUpper() )
{
    // Do something
}

This type of comparison has a flaw: with ToUpper you are creating an uppercase copy of the string. If you are comparing large strings or a lot of strings inside loops the performance penalty can be significant.

An alternative in this case is string.Compare(string, string, bool). This method returns 0 if the two strings are equals, a non zero value if the strings are different. Passing true to the boolean parameter allows us to specify that we want to perform a case insensitive comparison. This method however will give you different comparison results than the == operator, the reason for this is that this overload of the Compare method uses the current culture to do the comparison, while the == operator uses the raw binary value of the string.

A more appropriate overload of the same method to perform the comparison in the same way as the == operator is the string.Compare(string, string, StringComparison) method. Passing the value StringComparison.OrdinalIgnoreCase to the last parameter we will obtain the same comparison done by the == operator, but in a case insensitive way.

string a = "something";
string b = "Something else";

if( string.Compare(a, b, StringComparison.OrdinalIgnoreCase) == 0)
{
    // Do something
}

We can use the same overload of String.Compare to perform comparisons based on a Culture (also called locale). Passing as the last parameter StringComparison.CurrentCulture or StringComparison.CurrentCultureIgnoreCase we will do comparison using the current culture of the application, passing StringComparison.InvariantCulture or StringComparison.InvariantCultureIgnoreCase will do the comparison using the InvariantCulture (a culture based on the English language but country independent).

Please bear in mind that the methods using some Culture information are slower than the ones that do the comparison based on the raw value of the string.

An equivalent approach to use the string.Compare method is to use one of the default StringComparer (there is one matching each StringCompare value). StringComparer is a class implementing the IEqualityComparer generic interface for the type String. We can pass instances of this object to other objects to control how they perform string comparisons. For example we can easily create a dictionary where the key is a string and we want the key comparison to be case insensitive.

var dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);