Andy Crouch - Code, Technology & Obfuscation ...

Thoughts On C# Switch Statements

Photo: Michal Mrozek - Unsplash

C# has switch statements much like many other programming languages. While they can be a useful solution in some situations they can also be a code smell.

When should you use a switch statement? When it makes the code’s intention clear or the code easier to read. For example:

if(myEnumeration.Value == 1)
{
  // execute code for value 1

}
else if(myEnumeration.Value == 2 || myEnumeration.Value == 3)
{
  // execute code for values 2 or 3

}
else if(myEnumeration.Value == 4)
{
  // execute code for value 4

}
else if(myEnumeration.Value == 5)
{
  // execute code for value 5

}
else if(myEnumeration.Value == 6)
{
  // execute code for value 6

}
else
{
  // execute code for all other values

}

vs

switch(myEnumeration.Value)
{
  case 1:
    // execute code for value 1

  case 2:
  case 3:
    // execute code for values 2 or 3

  case 4:
    // execute code for value 4

  case 5:
    // execute code for value 5

  case 6:
    // execute code for value 6

  default:
    // execute code for all other values

}

As you can see the switch syntax is cleaner as it removes the need for so many brackets. It also supports “fall through” functionality which makes it easier to see mixed clauses (such as values 2 and 3). I personally like the default label which makes it clear what will be done if no values are matched. Switch statements are ideally suited to enumerations and numeric conditional tests. They are also ideally suited to instances where there is a single execution task based on a successful match.

When should you not use a switch statement? When you are working with non-numeric values. When you have multiple lines to execute on a successful match and when you can see (or smell) an Open Closed Principle violation such as the following code:

public IEnumerable<SubscriptionsModel> ApplySort(IEnumerable<SubscriptionsModel> list, string sortBy, bool asc)
{
  switch (sortBy)
  {
    case "firstname":
      return asc ? list.OrderBy(x => x.FirstName) : list.OrderByDescending(x => x.FirstName);
    case "lastname":
      return asc ? list.OrderBy(x => x.LastName) : list.OrderByDescending(x => x.LastName);
    case "email":
      return asc ? list.OrderBy(x => x.Email) : list.OrderByDescending(x => x.Email);
    case "company":
      return asc ? list.OrderBy(x => x.Company) : list.OrderByDescending(x => x.Company);
    case "typeofuser":
      return asc ? list.OrderBy(x => x.TypeOfUser) : list.OrderByDescending(x => x.TypeOfUser);
    case "telephone":
      return asc ? list.OrderBy(x => x.Telephone) : list.OrderByDescending(x => x.Telephone);
    case "createdate":
      return asc ? list.OrderBy(x => x.CreateOn) : list.OrderByDescending(x => x.CreateOn);
    default:
      return list.OrderBy(x => x.FirstName).ThenBy(x => x.LastName);
  }
}

As you can see this method is applying sorting to an enumerable SubscriptionsModel. Each case line is determining if it should order in ascending or descending order and the field to sort on. The way it is currently written means that if we add a new field into the SubscriptionModel class we would need to update the switch statement. This is far from ideal. An extra point I will make is that there is no case independent comparison here either. This is a reason I tend to not us switch statements with Strings. You have to sanitise the input as we know users can surprise use with unpredictable input.

Code like this is very common. However, C# offers a nice solution to the endless case branches, reflection. Using reflection will allow the above code to become:

public IEnumerable<SubscriptionsModel> ApplySort(IEnumerable<SubscriptionsModel> list, string sortBy, bool asc)
{
  var orderByColumnName = string.IsNullorWhiteSpace(sortBy) ? "FirstName" : sortBy;
  var orderByPropertyInfo = typeof(SubscriptionsModel).GetProperty(sortBy);

  return asc ? 
    list.OrderBy(x => orderByPropertyInfo.GetValue(x, null)) : 
    list.OrderByDescending(x => orderByPropertyInfo.GetValue(x, null));
}

This code uses a PropertyInfo instance which can be used to pass into the OrderBy and OrderByDescending methods. This allows dynamic rather than hard-coded parameters to be used removing the need to change the method when a new field is added. Mixing this into another C# favourite of mine, extensions, you might want to create a generic method such as

public static class EnumberableExtensions
{
  public static IEnumerable<T> Sort<T>(this IEnumerable<T> enumerable, string sortByField, SortOrder sortOrder)
  {
    if(String.IsNullOrWhiteSpace(sortByField))
      return enumerable;
    
    if(sortOrder < 0)
      return enumerable;
    
    var orderByPropertyInfo = typeof(T).GetProperty(sortByField);
    
    if(sortOrder == SortOrder.Ascending)
      return enumerable.OrderBy(x => orderByPropertyInfo.GetValue(x, null));
    
    return enumerable.OrderByDescending(x => orderByPropertyInfo.GetValue(x, null));
  }
}

This is a basic implementation. I have shared this with a mid-level developer recently as a starting point. The challenge was to write an implementation that contains no if statement for the sort direction.

This post has shown that you should think about the way in which switch statements are used. While useful, they need to be used in a way that doesn’t violate the SOLID principles. If you have any feedback or comments around this post then please contact me via twitter or email.