在编译时为switch语句生成常量字符串

3
我们在一个 .net 4.5(重要)的遗留代码库中,有一个静态类来定义很多 const 字符串值的对象类型(表示 x.GetType().ToString() 的值),主要用于 switch 语句。这是非常糟糕的,因为某些重构会破坏所有这些 switch 语句,而且使用这些 switch 语句的位置非常广泛,我们无法更改它。如果现在我要编写其他解决方案,我知道还有其他解决方案,但是:在不更改 switch 语句的情况下,是否有任何方法定义类型的 const 字符串以在编译时选择编译时类型,因为编译时具有所需的所有信息。我知道 switch 语句在编译时编译成查找表,并且不在 case 中评估表达式,但是否有任何方法在编译时一次性定义 const 值?我唯一能想到的是在生成代码之前动态生成代码。有其他解决方案吗?
1个回答

6

C# 6 引入了一个特性来解决这个问题,nameof 表达式.

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 {}

运行示例

nameof 中引用的 FooBar 是类型,如果您重命名类,则任何自动重构工具也会替换 nameof 中的类型。

编辑:没有注意到“不修改开关”部分,您也可以将其与常量字符串一起使用。

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;
    }
}
更新nameof 方法只返回类型名称而不包括命名空间,而 Type.ToString() 则包括命名空间。因此,可能无法使用该方法。
如果您无法使用 C# 6,则可以使用 T4 文本模板为 switch 语句动态构建常量。但是,唯一的问题是持有所引用类型的程序集不能存在于您正在生成代码的同一个程序集中。
以下代码假定您在同一个解决方案中有一个名为 DataTrasferObjects 的项目,其中包含名为FooBar的类。
<#@ 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() #>";
}

请注意,在每次构建时您需要配置您的构建服务器以自动重新生成代码。

我应该补充说明一下,我现在卡在了.net 4.5上,需要一个适用于该版本的解决方案。但是除了代码生成之外,我没有看到其他任何东西,而且那还非常复杂。我没有看到任何简单的解决方案。 - Sam
1
是的,我想不出任何非代码生成解决方案适用于C# 5或更低版本(FYI,您可以使用.NET 4.5的C# 6,您只需要通过使用VS2015附带的编译器进行构建来更改编译器)。编辑:如果我有时间并且没有其他人回答,我可能会尝试编写另一个答案,展示如何使用T4代码生成来完成此操作。 - Scott Chamberlain
我添加了一个 .net 版本,如果你有一个带有 T4 示例的问题,那将是很好的。 - Sam
我认为你还没有理解,nameof 不与 .net 版本绑定。它与你用于编译的 Visual Studio 版本有关。就像默认参数是在 .net 2.0 发布后添加的一样,但如果使用 VS2010 或更新版本进行构建,则可以在2.0中使用它们。 - Scott Chamberlain
好的,我知道了。但我们现在不能升级,涉及到的东西和人员太多了,一如既往。我正在寻找一种语言层面上的解决方案。项目并不总是那么简单。 - Sam
@Sam 更新了一个 T4 示例。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接