SMBs

Visual Studio compiler options, part 3: Option Compare

Option Compare is a compiler option in VB.NET with the right default for all new projects. Here's what SMBs need to know about the option.

Why it's important

There are several reasons you should give some thought to this option. Whether one is more important than another depends on what your needs and priorities are.

Equivalence

Because a Binary compare is case-sensitive, you must understand that with this option, all tests for equivalence will be case-sensitive. If that's what you want, all is well. If not, you will have to think about string comparisons everywhere you do them, and make sure you are comparing what you think you are comparing. You will need to override the default at local scope every time you want a case-insensitive check for equivalence.

The classic example is any time you check to see if one string equals another:

If FruitBasket.FruitType = Fruit.TypeEnum.Apple.ToString Then

MakeApplePie()

End If

"Apple" and "apple" are not the same in Binary compareland. So if you are going to stick to the default, and if you are going to compare strings, and if in the business rules of the use case "Apple" and "apple" are equivalent, you will have to override the default.

One way to do this is to dispense with the = operator altogether and just use one of the IComparable methods with an explicit setting of the comparison method -- something like this:

If FruitBasket.FruitType.Equals(Fruit.TypeEnum.Apple.ToString,  

StringComparison.OrdinalIgnoreCase) Then

MakeApplePie()

End If

If all string comparisons in one particular module are going to abide by the rule that "Apple" and "apple" are equivalent, you can set Option Compare to Text for the whole module and not mess with your = operators. It must be the first line in the file:

Option Compare Text

Imports System.Text

Imports System.Collections.Generic
Public Class AppleComparer

If FruitBasket.FruitType = Fruit.TypeEnum.Apple.ToString Then

MakeApplePie()

End If

End Class

Note that if you have more than one class defined in a file, the Option Compare statement still must go at the top of the file, and it will apply to all classes defined in that file. You cannot do this:

Imports System.Text
Imports System.Collections.Generic
Public Class Dessert

If FruitBasket.FruitType = Fruit.TypeEnum.Apple.ToString Then

MakeApplePie()

End If

End Class
Option Compare Text

Public Class Entree

If Pantry.IsEmpty Then

GoShopping()

End If

End Class

If your module is not so simple that you can set Option Compare Text for the entire file, or if you need case-insensitive comparisons to rule the day throughout the module, you might be tempted to convert the strings to upper- or lowercase before comparing them, but don't do it.

If FruitBasket.FruitType.ToUpper = Fruit.TypeEnum.Apple.ToString.ToUpper Then

MakeApplePie()

End If

That approach has two downsides. One, it converts the strings to a different case, thus creating new strings, a performance hit. Two, it will not work in languages that have different characters for different cases. You should use a binary comparison method with a StringComparison.OrdinalIgnoreCase parameter instead.

Wherever case sensitivity is a barrier to correct results in straight-up string comparisons, you will have to do something to ensure case-insensitive comparisons. That either means setting Option Compare to Text in your project, setting it to Text in your modules, or forcing case-insensitive comparisons at local scope.

Performance

It's nice to have so many different ways to compare strings in VB.NET, but they do not perform the same. To show you what I mean, I took the words "Macintosh" and "macintosh" and compared them in batches. In each batch, I compared the strings 10,000 times in loops of 100 iterations, netting 1,000,000 back-to-back comparisons per batch. I ran each batch several times (approximately 10 each), so I could get a range of results. Here is what I got for the average elapsed number of milliseconds per batch of 10,000 comparisons.

Operator Option Compare Setting Case Sensitivity Low Avg. High Avg.
= Text N/A 0.03 0.04
= Text ToUpper/Lower, but N/A 0.04 0.09
> Text N/A 0.03 0.04
> Text ToUpper/Lower, but N/A 0.04 0.10
Like Text N/A 0.49 0.65
Like Text ToUpper/Lower, but N/A 0.41 0.54
= Binary Case sensitive 0.0 0.0
= Binary ToUpper/Lower case insensitive 0.03 0.07
> Binary Case sensitive 0.0 0.0
> Binary ToUpper/Lower case insensitive 0.03 0.06
Like Binary Case sensitive 0.0 0.0
Like Binary ToUpper/Lower case insensitive 0.05 0.09
Equals Binary StringComparison.Ordinal 0.0 0.0
Equals Binary StringComparison.OrdinalIgnoreCase 0.0 0.0
CompareTo Binary StringComparison.Ordinal 0.01 0.02
CompareTo Binary StringComparison.OrdinalIgnoreCase 0.0 0.0

The ranges for ToUpper and ToLower were the same, so I'm listing them together. Some of the operators are so fast they didn't even register elapsed times. Others are much slower, with Like being the worst of all (by far) when using Option Compare Text.

Here's the same set of results when sorted from fastest to slowest.

Operator Option Compare Setting Case Sensitivity Low Avg. High Avg.
= Binary Case sensitive 0.0 0.0
> Binary Case sensitive 0.0 0.0
CompareTo Binary StringComparison.OrdinalIgnoreCase 0.0 0.0
Equals Binary StringComparison.Ordinal 0.0 0.0
Equals Binary StringComparison.OrdinalIgnoreCase 0.0 0.0
Like Binary Case sensitive 0.0 0.0
CompareTo Binary StringComparison.Ordinal 0.01 0.02
= Text N/A 0.03 0.04
> Text N/A 0.03 0.04
> Binary ToUpper/Lower case insensitive 0.03 0.06
= Binary ToUpper/Lower case insensitive 0.03 0.07
= Text ToUpper/Lower, but N/A 0.04 0.09
> Text ToUpper/Lower, but N/A 0.04 0.10
Like Binary ToUpper/Lower case insensitive 0.05 0.09
Like Text ToUpper/Lower, but N/A 0.41 0.54
Like Text N/A 0.49 0.65

What to do with it

I don't see any benefit in using Option Compare Text. The only thing it gives you is a case-insensitive comparison method that is slower than most of your Binary alternatives, and much slower than some of them. The Like operator under Option Compare Text is a true slowpoke. The others are better, but not good enough to compete. You can leave Option Compare at its default of Binary without any downsides.

But even if you're an Option Compare Binary junkie, this exercise points out the downside to several of your comparison options. ToUpper and ToLower are much slower than the StringComparison alternatives. It may be that the difference is not significant in your applications; that would certainly be the case in a desktop application where string comparisons are few. But in a multi-user application or an application running on an underpowered device, you're better off going with the much faster alternatives. And there's really no benefit in habitually using a slow string comparison method.

Exceptions and variations

The only scenario I can see recommending that you stick with Option Compare Text is the legacy one. If your boss hands you an application that has Option Compare set to Text, either for the whole project or for a module, make sure you review the code before you change it to Binary. Once you do, the application will behave differently, and you'll want to make sure you've deployed appropriate alternatives at all such points before making the switch.

Conclusion

Option Compare is a compiler option in VB.NET with the right default for all new projects. Leave it alone. Then scour your code for string-comparison bottlenecks and implement the much-faster alternatives available to you.

0 comments

Editor's Picks