switch变量声明如何作用域?

时间:2017-10-22 17:54:11

标签: c# scope switch-statement variable-declaration c#-7.0

如何追随?

switch (param.ParameterType)
{
    case Type x when x == typeof(byte):
        int invalid;
        break;
    case Type x when x == typeof(short):
        int invalid;
        break;
    case Type x when x == typeof(int):
        break;
    case Type x when x == typeof(long):
        break;
}

问题是,x如何在每个案例范围内没有任何可见的块。同时,变量invalid不能在不同的开关情况下声明。它必须在一个街区内。

如果没有阻塞,则无法对变量进行范围界定。

{
    // case byte:
    Type x;
    int invalid;
    // break;

     // case short:
    Type x; // x can not be declared twice.
    int invalid;
}

如果每个案例都有不可见的块,则必须遵循以下规则(但不是)。

{
    {   // block for each case.
        // case byte:
        Type x;
        int invalid;
        // break;
    }

    {
        // case short:
        Type x;
        int invalid; // but this throws compile time error.
    }
}

似乎编译器在这里做了一些魔术,显然x的范围与invalid变量不同。这是编译器语义分析阶段的错误吗?

1 个答案:

答案 0 :(得分:8)

  

问题是,如何在每个案例中使用x作用域而没有任何可见的块。同时,变量无效不能在不同的开关情况下声明。它必须在一个街区内。

在案例标签中通过模式匹配引入的变量仅具有该案例的主体范围。

在case主体中“正常”引入的变量具有整个switch语句的范围。

是的,这是不一致的 - 但我认为:

  • 能够通过模式匹配引入多个具有相同名称的变量
  • 特别有用
  • case语句中引入的变量范围是一个设计错误,这只是为了防止错误进一步发展

请注意,对于使用相同大小写块的情况,您不能使用模式匹配多次声明同一变量。例如,通过简化代码,这很好:

object o = null;
switch (o)
{
    case Type x when x == typeof(byte):
        break;
    case Type x when x == typeof(short):
        break;
}

但这不是:

object o = null;
switch (o)
{
    case Type x when x == typeof(byte):
    case Type x when x == typeof(short):
        break;
}

可以说编译器可以有一些规则允许你引入多个变量,只要它们属于同一类型 - 这对于公共代码来说非常方便。但它肯定会使语言变得更加复杂......

作为“设计错误”点的一个例子,C#5规范实际上由于它而产生错误。 C#5规范(8.7.2)声称:

  

“不通过”规则可以防止在意外省略break语句时C和C ++中出现的常见错误类。另外,由于这个规则,switch语句的switch部分可以任意重新排列,而不会影响语句的行为。

由于模式匹配排序,这种“任意重新排列”在C#7中是不真实的,但它始终是不真实的。请考虑以下代码:

class Test
{
    static void Main(string[] args)
    {
        switch (args.Length)
        {
            case 0:
                string x;
                break;
            case 1:
                x = args[0];
                break;
        }
    }
}

由于奇怪的范围规则,这是有效的 - x在范围内,可用于“案例1”块。但是,如果您重新安排案例:

class Test
{
    static void Main(string[] args)
    {
        switch (args.Length)
        {
            case 1:
                x = args[0]; // Invalid
                break;
            case 0:
                string x;
                break;
        }
    }
}

...现在这会产生编译时错误。变量仍然在范围内(编译器知道x的含义)但是在声明之前你不能为局部变量赋值。

据我所知,没有人希望使用早期作用域声明的变量 - 对于每个case块引入新的变量声明会更有意义空间,或C#要求案件块的大括号。