Generate const strings at compile time for switch cases

1.3k views Asked by At

We have in a legacy code base on .net 4.5 (important) a static class which defines many const string values of object types (means the values of x.GetType().ToString()) mainly for usage in switch statements.

This is especially bad because certain refactorings break all those switch statements and the places where this is used is so vast that we can't change it. I know other solutions if I would write it now, but:

Is there any way - without changes the switch statements - to define the const strings of types to pickup the compile time type since I have all information I need at compile time.

I know that switch statements are compiled at compile time into a lookup table and do not evaluate expression in cases, but is there any way to define an const value once at compile time? The only thing I can think of is to dynamically generate code before the build. Is there any other solution?

1

There are 1 answers

6
Scott Chamberlain On BEST ANSWER

C# 6 is introducing a feature to solve this exact problem, the nameof expression.

using System;

public class Program
{
    public static void Main()
    {
        Test(new Foo());
        Test(new Bar());
    }

    private static void Test(object x)
    {
        switch(x.GetType().ToString())
        {
            case nameof(Foo):
                Console.WriteLine("Inside Foo's code");
            break;

            case nameof(Bar):
                Console.WriteLine("Inside Bar's code");
            break;
        }
    }
}

public class Foo {}
public class Bar {}

Run Example

the Foo and Bar referenced in the nameof are types, if you rename the class any automatic refactoring tools will also replace the type in the nameof.

EDIT: Did not catch the "do not modify the switch" part, you can use it with constant strings too.

const string FooName = nameof(Foo);
const string BarName = nameof(Bar);

private static void Test(object x)
{
    switch(x.GetType().ToString())
    {
        case FooName:
            Console.WriteLine("Inside Foo's code");
        break;

        case BarName:
            Console.WriteLine("Inside Bar's code");
        break;
    }
}

UPDATE: nameof does not return the full name with namespace only the type name, where Type.ToString() does include the namespace. So maybe this would not work.


If you can't use C# 6 another option is to use T4 Text templates to dynamically build your constants for your switch statements. The only issue with this is the assembly that holds the types you are referencing can not exist in the same assembly that you are generating the code in.

The following code assumes you have a project named DataTrasferObjects in the same solution with classes named Foo and Bar

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="$(SolutionDir)\DataTrasferObjects\bin\Debug\DataTrasferObjects.dll" #>
<#@ import namespace="System" #>
<#@ output extension=".cs" #>

<#
    Type fooType = typeof(DataTrasferObjects.Foo);
    Type barType = typeof(DataTrasferObjects.Bar);
#>

public static class Names
{
    public const string FooName = "<#= fooType.ToString() #>";
    public const string BarName = "<#= barType.ToString() #>";
}

Note, you will need to configure your build server to automatically regenerate the code on each build.