Recently we got a feature request from a client who was fighting with large switch statements, such as IsInTypeOnlyContext in the Roslyn CSharp SyntaxFacts helper. Here is a shorter version of the large switch statement:

switch (parent.Kind())
{
    case Attribute:
        return ((AttributeSyntax)parent).Name == node;
    case PointerType:
        return ((PointerTypeSyntax)parent).ElementType == node;
    case NullableType:
        return ((NullableTypeSyntax)parent).ElementType == node;
}

The client was looking for a refactoring that would sort the case labels that way:

switch (parent.Kind())
{
    case Attribute:
        return ((AttributeSyntax)parent).Name == node;
    case NullableType:
        return ((NullableTypeSyntax)parent).ElementType == node;
    case PointerType:
        return ((PointerTypeSyntax)parent).ElementType == node;
}

In fact we also need that refactoring here and there writing our own JustCode analyzers.

It is already implemented, so unless we find some critical issue it should be released with the next internal build. It will be available only for Visual Studio 2015 as it is implemented as Roslyn refactoring provider, thus available in the fixes section of the Visual Aid.

Techincal details

Let’s start with what a switch statement is from Roslyn perspective. A switch statement consists of a switch keyword, an open paren token, an expression, a close paren token, an open brace token, sections and a close brace token (in that order). We are interested only in the sections as they need to get sorted. Now let’s see what a section is. The following samples show what is considered a section, basically a set of labels and the statements they share.

case Attribute:
    return ((AttributeSyntax)parent).Name == node;
case Attribute:
case NullableType:
    return null;
case default:
    Console.WriteLine("Default case");
    return null;

The case with every section having only one label is quite straightforward, so let’s consider another case where labels not in the correct order share the same statements, as in here:

switch (name)
{
    case "John"
    case "Anthony":
        return "Male";
    case "Billy":
        return "Female";
}

Here the correct order would be “Anthony”, “Billy”, “John”. We basically had two options:

  • Keep the statements intact and use the first label as a sort key.
switch (name)
{
    case "Anthony":
    case "John"
        return "Male";
    case "Billy":
        return "Female";
}
  • Sort the labels in the correct order and copy the statements if necessary.
switch (name)
{
    case "Anthony":
        return "Male";
    case "Billy":
        return "Female";
    case "John":
        return "Male";
}

We decided to go with the second option as we find it more relevant to what that refactoring should do. In the end you would want your labels in the correct order so you can reason about the flow.

An important note: If labels are in one section and remain in the same section after sorting, they will in fact be still in the same section, for example:

switch (name)
{
    case "Billy":
    case "Anthony":
        return "Cool";
    case "John":
        return "Not cool";
}

will be refactored to

switch (name)
{
    case "Anthony":
    case "Billy":
        return "Cool";
    case "John":
        return "Not cool";
}

Any suggestions would be welcomed in the comments section below.