考虑以下代码:
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针对新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;
}
相当于这个伪代码(忽略顺序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_4
到case_1
的跳跃被定义为导致fs4
的处置 - 。。。还不清楚。。。
- 当控件遇到
goto case_4
时(在case_1:
之后(是否应立即处置fs1
- 当从
case_1:
跳到case_4:
时,是否应该初始化fs3
,因为它在范围内(忽略它没有使用的事实( fs1
在case 3
和case 4
中是否应该初始化,尽管严格来说它在范围内
- 当控件遇到
因为链接的规范建议只显示一个向后goto
到一个using
块之前的点(因此using
语句的主题将不在范围内(,而在这种情况下,当向前跳转时,fs1
和fs3
是否仍在范围内(或不在(还没有明确定义。
请记住,using;
表示,当对象超出作用域时,应该对其进行处理,而不是说,当对象最后一次在其声明的作用域中使用时,应该将其进行处理(这将禁止将其传递给仍在使用它的另一个方法(。
对于可能/应该发生的事情,至少有两个论点:
一旦
fs1
跳到case_3
,就立即处理它,即使fs1
在技术上仍在作用域中(如果您订阅了"所有案例共享相同的作用域"学派(。- 这也忽略了
using;
语句的要点,该语句将其主体的生存期严格绑定到封闭范围
- 这也忽略了
当控制离开整个
switch
块时,仅处理fs1
(即使可以说fs1
在此之前超出了范围(。
正如提案所提到的,考虑到语言设计团队所面临的时间限制,这是一件本可以敲定的事情,但可能不会得到人们的同意。
您需要限制变量的范围
switch ("")
{
case "":
using(var s = new MemoryStream())
{
// SCOPE of var S
}
// break can be inside or outside the braces
break;
}