VersionString (eg "1.0.2") IComparer algorithm For a project I'm working on, I need to compare version strings where:


version strings are composed only of numbers and periods
version strings are made up of an arbitrary number of segments (major.minor, manjor.minor.build, etc)
1.0 is equivalent to 1.0.0, etc.


Please provide feedback on performance considerations or style.

Here's my shot at it:

public class VersionStringComparer : IComparer&lt;string&gt;
{
    public int Compare(string x, string y)
    {
        if (x == y) return 0;

        var xparts = x.Split('.');
        var yparts = y.Split('.');

        var length = new[] {xparts.Length, yparts.Length}.Max();

        for (var i = 0; i &lt; length; i++)
        {
            int xint;
            int yint;

            if (!Int32.TryParse(xparts.ElementAtOrDefault(i), out xint)) xint = 0;
            if (!Int32.TryParse(yparts.ElementAtOrDefault(i), out yint)) yint = 0;

            if (xint &gt; yint) return 1;
            if (yint &gt; xint) return -1;
        }

        //they're equal value but not equal strings, eg 1 and 1.0
        return 0;
    }
}


And two quick unit tests (using the Should.Fluent library:

    [TestMethod]
    public void VersionStringComparerSortsMajorAndMinorVersionString()
    {
        var versions = new[]{"1.5","1.0","2.0","1.0"};
        var versionStringComparer = new ReleaseGateway.Util.VersionStringComparer();

        var sorted = versions.OrderBy(x =&gt; x, versionStringComparer).ToArray();

        var expected = new[] {"1.0", "1.0", "1.5", "2.0"};
        sorted.Should().Equal(expected);
    }

    [TestMethod]
    public void VersionStringComparerSortsArbitraryRadixVersionStrings()
    {
        var versions = new[] { "1.5.4", "1.7", "1.0", "1","1.4" };
        var versionStringComparer = new ReleaseGateway.Util.VersionStringComparer();

        var sorted = versions.OrderBy(x =&gt; x, versionStringComparer).ToArray();

        var expected = new[] { "1.0", "1", "1.4", "1.5.4", "1.7" };
        sorted.Should().Equal(expected);
    }
I adjusted the compare method slightly, making it more readable (IMO) through the use of an Anonymous type with named properties and by extracting two helper methods.

I'm not checking the result of TryParse anymore since the result is 0 for failed conversions.
Regarding performance: on my machine, your version runs ten thousand iterations in ~400ms, while mine seems to shave off around one fourth, running the same amount of iterations at ~300ms.   

This should be because I moved the parsing of the versions out of the loop.
I measured this using a System.Diagnostics.Stopwatch. 

EDIT: A note on performance... micro-optimizations such as the ones I've done are meaningless unless you have actually identified a real bottleneck in your code. I only ran these simplified benchmarks to make sure I wasn't making your implementation slower while getting it into a more expressive form.

public int Compare(string x, string y)
{
    if (x == y) return 0;
    var version = new { First = GetVersion(x), Second = GetVersion(y) };
    int limit = Math.Max(version.First.Length, version.Second.Length);
    for (int i = 0; i &lt; limit; i++)
    {
        int first = version.First.ElementAtOrDefault(i);
        int second = version.Second.ElementAtOrDefault(i);
        if (first &gt; second) return 1;
        if (second &gt; first) return -1;
    }
    return 0;
}

private int[] GetVersion(string version)
{
    return (from part in version.Split('.')
            select Parse(part)).ToArray();
}

private int Parse(string version)
{
    int result;
    int.TryParse(version, out result);
    return result;
}


This adapted converter returns the same test results as your original one - though I haven't tried to test it more thoroughly - maybe someone can spot an edge case? ;-)