C#: Compare Application Version Numbers


UPDATE: As discussed in the comments, 99% of the time using the System.Version class included in the .net Framework will suffice for comparing application version numbers. Documentation for this can be found here. The below post will help you if for some reason (ex. you use commas instead of periods to separate the version number) the System.Version class will not work

When incorporating an automatic update feature into an application, one of the things that you will need to do is compare the installed version number to the currently available version number. Given version numbers of the format ‘X.X.X.X’ I have seen some people suggest simply removing the periods, converting the string to an integer and doing a simple comparison of the integers. Such a solution works great if you have complete control over the format of the version number and you can ensure the number of digits per grouping stays the same. But, suppose you have the following two version numbers: 1.4.0 and 1.1.28. Following the logic explained above, you will be comparing the integers 140 and 1128 and the second version will be determined to be greater than the first, which is incorrect.

In order to alleviate this problem, I have created the following method below to correctly compare two version numbers. This method uses the SplitToList extension which takes a string, splits it based on a given string and then converts each element to the specified type. You can find a more detailed explanation and the code here.

public static int CompareVersionNumbers(string versionA, string versionB)
{           
    // Convert each version numbers string to a list of integers
    List<int> a = versionA.SplitToList<int>(".");
    List<int> b = versionB.SplitToList<int>(".");            
   
    // Ensure that each of the lists are the same length
    while(a.Count < b.Count) { a.Add(0); }
    while(b.Count < a.Count) { b.Add(0); }

    // Compare elements of each list
    for (int i = 0; i < a.Count; i++)
    {
        // If the element from list A is greater than B, 
        // versionA is greater than versionB and visa versa.
        // If they are equal, go to the next element.
        if (a[i] > b[i])
            return 1;
        else if (b[i] > a[i])
            return -1;
    }

    // If we reach this point, the versions are equal
    return 0;
}

C#: String to Integer, Decimal, Float, or Any Type of Array or List


The string.Split method is a great tool that can be used when manipulating strings. But, this method simply returns an array of strings when frequently I need a collection returned in the type that the strings actually represent. To fix this you simply have to loop through each item in the string array and convert it to its representative type. For example, if you are reading in a comma delimited file of temperatures, you can split on the commas and then loop through the returned array, parsing each element and adding it to a new list, as follows:


string temperatures = "67.2,92.1,78.2,100.3,89.2";

string[] tempArray = temperatures.Split(',');

List<float> temps = new List<float>();
foreach (string temp in tempArray)
    temps.Add(float.Parse(temp));

While such a solution is common and quite acceptable, I wanted to make it simpler and reusable. To do such, I decided to implement the above logic in a generic extension method.


/// <summary>
/// Splits a string using the supplied separator and casts each element to the
/// indicated type.
/// </summary>
/// <typeparam name="T">The type of the List to return</typeparam>        
/// <param name="s">The string on which the operation will be performed.</param>
/// <param name="separator">An array of strings that delimit the substrings in this string, an empty
///     array that contains no delimiters, or null.</param>
/// <returns>A List of type T containing the elements formed after splitting the string using
/// the given separators.</returns>
public static List<T> SplitToList<T>(this string s, params string[] separator)
{
    return s.SplitToList<T>(StringSplitOptions.None, separator);
}

/// <summary>
/// Splits a string using the supplied separator and casts each element to the
/// indicated type.
/// </summary>
/// <typeparam name="T">The type of the List to return</typeparam>        
/// <param name="s">The string on which the operation will be performed.</param>
/// <param name="options">Specify System.StringSplitOptions.RemoveEmptyEntries to omit empty array
///     elements from the array returned, or System.StringSplitOptions.None to include
///     empty array elements in the array returned.</param>
/// <param name="separator">An array of strings that delimit the substrings in this string, an empty
///     array that contains no delimiters, or null.</param>
/// <returns>A List of type T containing the elements formed after splitting the string using
/// the given separators.</returns>
public static List<T> SplitToList<T>(this string s, StringSplitOptions options, params string[] separator)
{
    // Split the string based on the supplied separators
    string[] array = s.Split(separator, options);
    
    List<T> values = new List<T>();

    // Convert each element in the array to the indicated type
    foreach (string element in array)
        values.Add((T)Convert.ChangeType(element, typeof(T)));                

    return values;
}

Now our temperature example can be reduced to the following:


string temperatures = "67.2,92.1,78.2,100.3,89.2";
List<float> temps = temperatures.SplitToList<float>(",");

Below are some other examples of how this extension method could be used:

string s1 = "true-false-true-true-true-false";
List<bool> bools = s1.SplitToList<bool>("-");

string s2 = "a b c d e f g h i j";
List<char> chars = s2.SplitToList<char>(" ");

// Split on both commas and periods
string s3 = "1,3,4.5.7,1.4,6.7,8.2";
List<int> ints = s3.SplitToList<int>(",", ".");

C#: DateTime.ToString() Format – Apostrophe Before the Year


I was trying to use the DateTime.ToString(string) method to create a string that had the abbreviated month and last two digits of the year but I wanted to add an apostrophe before the year to produce something like Sep ’10 for September 2010. It took me a while to figure our the correct escaping sequence but here it is.

DateTime date = DateTime.Today;
Console.WriteLine(date.ToString("MMM \"'\"yy"));

// Output
// ------
// Sep '10

Any text between the two escaped quotation marks (\”…\”) will be printed as literal text.

C#: List.Contains Method – Case Insensitive


I had a list of strings and needed to check whether or not a specific string was contained within the list ignoring the character casing.  Pre-LINQ days you would have had to loop through the entries calling the .ToLower or ToUpper method on each of the elements or using the string.Compare method.  But thanks to LINQ, one simple method will take care of this for us:

List<string> list = new List<string>() { "a", "b", "c", "d", "e" };
string value = "A";

if (list.Contains(value, StringComparer.OrdinalIgnoreCase))
    Console.WriteLine(value + " is in the list!");
else
    Console.WriteLine(value + " is not in the list!");

C#: Truncate a String at the End of a Word


It seems that just about everyone in the world has their own version of this method so I thought I would share mine as well. 

public static string Truncate(this string s, int length, bool atWord, bool addEllipsis)
{
     // Return if the string is less than or equal to the truncation length
     if (s == null || s.Length <= length)
          return s;

     // Do a simple tuncation at the desired length
     string s2 = s.Substring(0, length);

     // Truncate the string at the word
     if (atWord)
     {
          // List of characters that denote the start or a new word (add to or remove more as necessary)
          List<char> alternativeCutOffs = new List<char>() { ' ', ',', '.', '?', '/', ':', ';', '\'', '\"', '\'', '-' };

          // Get the index of the last space in the truncated string
          int lastSpace = s2.LastIndexOf(' ');

          // If the last space index isn't -1 and also the next character in the original
          // string isn't contained in the alternativeCutOffs List (which means the previous
          // truncation actually truncated at the end of a word),then shorten string to the last space
          if (lastSpace != -1 && (s.Length >= length + 1 && !alternativeCutOffs.Contains(s.ToCharArray()[length])))
               s2 = s2.Remove(lastSpace);
     }

     // Add Ellipsis if desired
     if (addEllipsis)
          s2 += "...";

     return s2;
}

This method returns a truncated string cut off at the nearest “end of word” location (the returned string will always be less than or equal to the length parameter).  The twist that I put on my version is that you can specify alternative cut offs rather that just spaces.  To illustrate the benefit of this, see the code below.

string s = "I like green, red, and yellow!";
string s2 = s.Truncate(12, true, false);

A straight forward truncation at the nearest space would result in the string “I like” as the last space before character 12 occurs right after the word “like”. But, ideally we want to truncate after green as that is the end of a word. In the code above, we check the next character after the ‘n’ in green and realize it is a comma, an alternative cut off, and preserve the word “green”.

For all you die-hard regex guys out there I do realize this can be done with less lines of code using regex, but this example doesn’t!

This method also takes advantage of the extension methods provided by the .Net Framework 3.5, if you didn’t notice already.

Follow

Get every new post delivered to your Inbox.

Join 69 other followers