首页 > 解决方案 > 为什么不能在 C# 中没有大括号的 switch 部分中使用 using 变量?

问题描述

考虑以下代码:

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

            break;
    }

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

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

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

标签: c#switch-statementgrammarusing-statement

解决方案


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

由于其实际生命周期的复杂性,using直接在标签内声明是非法的。case一种可能的解决方案是简单地赋予它与out var在同一位置的 a 相同的寿命。人们认为功能实现的额外复杂性和解决工作的容易性(只需在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;
}

...相当于这个伪代码(忽略顺序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_4to的跳转case_1被明确定义为导致fs4's 处置......
  • ……不清楚……
    • 控件遇到时是否fs1应立即处理goto case_4(后case_1:
    • 是否fs3应该在从case_1:to跳转时初始化,case_4:因为它会在范围内(忽略它未使用的事实)。
    • 是否fs1应该在case 3和中初始化case 4,即使严格来说它在范围内。

因为链接的规范提案只显示了一个 之前goto的一个点(因此该语句的主题将超出范围),而在这种情况下,它没有明确定义,如果并且仍然在范围内(或不在范围内)向前跳跃的时候。usingusingfs1fs3

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

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

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

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

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


推荐阅读