为什么不能在 C# 中没有大括号的 switch 部分中使用 using 变量?

Why can't a using variable be used within a switch section without braces in C#?

考虑以下代码:

    switch ("")
    {
        case "":
            using var s = new MemoryStream();

            break;
    }

以上代码不会编译并出现此错误:“无法在开关部分直接使用 using 变量(考虑使用大括号)”。

修复已经在建议中,但我的问题是为什么下面的代码是合法的而上面的不是?为什么 C# 不能只允许以前的代码?

    switch ("")
    {
        case "":
            {
                using var s = new MemoryStream();
            }
            
            // break can be inside or outside the braces
            break;
    }

C# 8.0's language proposal for the new using statement给出了这样的解释:

A using declaration is illegal directly inside a case label due to complications around its actual lifetime. One potential solution is to simply give it the same lifetime as an out var in the same location. It was deemed the extra complexity to the feature implementation and the ease of the work around (just add a block to the case label) didn't justify taking this route.


举个例子,考虑这个...

switch( foo )
{
case 1: // Yeah, I'm in the tiny minority who believe `case` statements belong in the same column as the `switch` keyword.
case 2:
    using FileStream fs1 = new FileStream( "foo.dat" );
    goto case 4;

case 3:
    using FileStream fs3 = new FileStream( "bar.dat" );
    goto case 1;

case 4:
    using FileStream fs4 = new FileStream( "baz.dat" );
    if( GetRandomNumber() < 0.5 ) goto case 1;
    else break;
}

...等同于此伪代码(忽略顺序 if 逻辑):

if( foo == 1 || foo == 2 ) goto case_1;
else if( foo == 3 ) goto case_3;
else if( foo == 4 ) goto case_4;
else goto after;

{
case_1:
    using FileStream fs1 = new FileStream( "foo.dat" );
    goto case_4;

case_3:
    using FileStream fs3 = new FileStream( "bar.dat" );
    goto case_1;

case_4:
    using FileStream fs4 = new FileStream( "baz.dat" );
    if( GetRandomNumber() < 0.5 ) goto case_1;
    else goto after;
}
after:

...规范说“与在同一位置的 using 语句中声明变量具有相同的效果。”,所以如果我正确理解规范,上面的代码就是与此相同:

if( foo == 1 || foo == 2 ) goto case_1;
else if( foo == 3 ) goto case_3;
else if( foo == 4 ) goto case_4;
else goto after;

{
case_1:
    using( FileStream fs1 = new FileStream( "foo.dat" ) )
    {
        goto case_4;

case_3:
        using( FileStream fs3 = new FileStream( "bar.dat" ) )
        {
            goto case_1;
        }
        
case_4:
        using( FileStream fs4 = new FileStream( "baz.dat" ) )
        {
            if( GetRandomNumber() < 0.5 ) goto case_1;
            else goto after;
        }
    }
}
after:

认为问题是:

  • 虽然从 case_4case_1 的跳转被明确定义为导致 fs4 的处理...
  • ……不清楚……
    • 控件遇到goto case_4时(case_1:之后)是否立即处理fs1
    • 当从 case_1: 跳转到 case_4: 时,是否应该初始化 fs3 因为它在范围内(忽略它未被使用的事实)。
    • 是否应在 case 3case 4 中初始化 fs1,即使严格来说它在范围内。

因为链接的规范提案只显示了一个backwards gotobefore一个using块(因此 using 语句的主题将超出范围),而在这种情况下,如果 fs1fs3 仍在范围内(或不在范围内),则定义不明确当向前跳跃.

请记住,using; 说该对象应该在超出范围时被释放,而不是说它应该在它最后一次在它声明的范围内使用时被释放(这将禁止传递它到另一种仍在使用它的方法)。

对于 could/should 发生的事情至少有两个论点:

  1. 一旦 fs1 跳转到 case_3,就立即处置 fs1,即使 fs1 在技术上仍在范围内(如果您订阅了“所有案例”共享同一范围”思想流派)。

    • 这也忽略了 using; 语句的要点,该语句将其主题的生命周期严格绑定到封闭范围。
  2. 仅在控制离开整个 switch 块时处理 fs1 (尽管可以说 fs1 是 out-在此之前的范围内。

正如提案中提到的,它是可以敲定的东西,但考虑到语言设计团队的时间限制,人们可能不会同意