Is there a less convoluted way to compare file versions?
Asked Answered
A

2

7

I wrote a function to compare file versions between what a client currently has and the latest version of the file on a server. The client passes the "quad" (Major.Minor.Build.Private) version number as a string to the server, and then the server uses FileVersionInfo:

// clientFileVersion will be in "quad" format, a la "3.1.4.1"
private bool ServerFileIsNewer(string clientFileVersion, FileVersionInfo serverFile)
{
    // Don't say I never learned nuthin' from Steve McConnell
    const int MAJOR_INDEX = 0;
    const int MINOR_INDEX = 1;
    const int BUILD_INDEX = 2;
    const int PRIVATE_INDEX = 3;

    string[] versionStrArray = clientFileVersion.Split('.');
    int FileMajorPartClient;
    Int32.TryParse(versionStrArray[MAJOR_INDEX], out FileMajorPartClient);
    int FileMinorPartClient;
    Int32.TryParse(versionStrArray[MINOR_INDEX], out FileMinorPartClient);
    int FileBuildPartClient;
    Int32.TryParse(versionStrArray[BUILD_INDEX], out FileBuildPartClient);
    int FilePrivatePartClient;
    Int32.TryParse(versionStrArray[PRIVATE_INDEX], out FilePrivatePartClient);

    int FileMajorPartServer = serverFile.FileMajorPart;
    int FileMinorPartServer = serverFile.FileMinorPart;
    int FileBuildPartServer = serverFile.FileBuildPart;
    int FilePrivatePartServer = serverFile.FilePrivatePart;

    return ((FileMajorPartClient < FileMajorPartServer) ||
           ((FileMajorPartClient == FileMajorPartServer) && (FileMinorPartClient < FileMinorPartServer)) ||
           ((FileMinorPartClient == FileMinorPartServer) && (FileBuildPartClient < FileBuildPartServer)) ||
           ((FileBuildPartClient == FileBuildPartServer) && (FilePrivatePartClient < FilePrivatePartServer)));
}

But then I realized that my megaboolean return statement would fail if, say, the client version was 2.1.1.1 and the server version was 1.1.2.1

IOW, it would indicate that the server version was newer when, in fact (of course) it is not.

So then I thought I would add more boolean logic such as:

if   (FileMajorClient > FileMajorServer) || 
     ((FileMajorClient == FileMajorServer) && (FileMinorClient > FileMinorServer)) ||
     ((FileMajorClient == FileMajorServer) && (FileMinorClient == FileMinorServer) && (FileBuildClient > FileBuildServer))
{
    return false;
}
else
{
    return ((FileMajorPartClient < FileMajorPartServer) ||
           ((FileMajorPartClient == FileMajorPartServer) && (FileMinorPartClient < FileMinorPartServer)) ||
           ((FileMinorPartClient == FileMinorPartServer) && (FileBuildPartClient < FileBuildPartServer)) ||
           ((FileBuildPartClient == FileBuildPartServer) && (FilePrivatePartClient < FilePrivatePartServer)));
}

...but as you can see, it's turning into a big ball of mad/spaghetti mess madness.

There has to a better, easier, more grokkable/maintainable way of comparing two file versions. But what/how?

UPDATE

Using Servy's answer, this is the method now:

private bool ServerFileIsNewer(string clientFileVersion, FileVersionInfo serverFile)
{
    Version client = new Version(clientFileVersion);
    Version server = new Version(string.Format("{0}.{1}.{2}.{3}", serverFile.FileMajorPart, serverFile.FileMinorPart, serverFile.FileBuildPart, serverFile.FilePrivatePart));
    return server > client;
}

Clean as an unblown whistle, concise as Joe Friday's dream witness, and as elegant as Grace Kelley.

Allistir answered 20/2, 2014 at 16:52 Comment(2)
without instance, if( Version.Parse("1.1.2.1") > Version.Parse("2.1.2") )Lapides
Small improvement suggestion: Version server = new Version(serverFile.FileMajorPart, serverFile.FileMinorPart, serverFile.FileBuildPart, serverFile.FilePrivatePart); There's no need for a string-roundtrip.Alexanderalexandr
T
29

Use the Version class:

Version first = new Version("1.1.2.1");
Version second = new Version("2.1.1.1");
bool b = first >= second;
Tapetum answered 20/2, 2014 at 16:54 Comment(0)
S
3

Servy's answer is the one you should go with.

However, for other situations when you have nested criteria and you don't have the benefit of a class that already implements the comparison, the most straightforward way to do it is to use multiple conditionals and "early out" as soon as you can. For example, when checking versions you could do this:

if (FileMajorClient > FileMajorServer)
    return false;
if (FileMajorClient < FileMajorServer)
    return true;

// Major versions are equal, now do the same thing for minor part
if (FileMinorClient != FileMinorServer)
    return (FileMinorClient < FileMinorServer);

Note that the major version check could have been written the same way as the minor version check. It's just two different ways of writing the same logic.

You can then do that for each of the remaining parts.

if (FileBuildPartClient != FileBuildPartServer)
    return (FileBuildPartClient < FileBuildPartServer);

return (FilePrivatePartClient <= FilePrivatePartServer);

At each step you have eliminated the cases where the client and server versions don't match.

This isn't as "clever" as writing a monolithic conditional statement, but it's easy to understand and easy to prove correct.

Stockpile answered 20/2, 2014 at 17:15 Comment(1)
Thanks, Jim; as a sage person once wrote, "clever-clever tricks" are (paraphrasing now) for the birds. My baroque Beethovian method has been reduced to a jazz fill (and I like that).Allistir

© 2022 - 2024 — McMap. All rights reserved.