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

Luk*_* Vo 5 c# grammar using-statement switch-statement

考虑以下代码:

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

            break;
    }
Run Code Online (Sandbox Code Playgroud)

上面的代码不会编译并显示以下错误:“A using 变量不能直接在 switch 部分中使用(考虑使用大括号)”。

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

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

Dai*_*Dai 4

C# 8.0 对新using语句的语言提案给出了这样的解释:

由于其实际生命周期的复杂性,直接在标签内声明using是非法的。case一种可能的解决方案是简单地赋予它与out var同一位置的 相同的生命周期。人们认为功能实现的额外复杂性和解决方法的简便性(只需在标签上添加一个块case)并不能证明采取这条路线是合理的。


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

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;
}
Run Code Online (Sandbox Code Playgroud)

...相当于这个伪代码(忽略顺序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:
Run Code Online (Sandbox Code Playgroud)

...规范说“与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:
Run Code Online (Sandbox Code Playgroud)

认为问题是:

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

因为链接的规范提案仅显示向后 到块之前goto的点(因此该语句的主题将超出范围),而在这种情况下,它没有明确定义 if和仍在范围内(或不在范围内)向前跳跃时。usingusingfs1fs3

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

对于可能/应该发生的事情至少有两个论据:

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

    • using;这也忽略了将其主题的生命周期严格绑定到封闭范围的语句的要点。
  2. fs1 当控制权离开整个块时才进行处置switch(即使可以说fs1在此之前超出了范围)。

正如提案中提到的,这是可以敲定的事情,但考虑到语言设计团队所面临的时间限制,人们可能不会同意这一点。