using (T resource = expression)
embedded-statement
编译器将其翻译为以下代码:
{
T resource = expression;//Shouldn't this statement be moved inside the try block?
try
{
embedded-statement
}
finally
{
if (resource != null)
((IDisposable)resource).Dispose();
}
}
我的问题是:为什么在try块周围有一个额外的{}
?第一个语句不应该移到try块中吗?
MSDN解释道:
前面的代码示例在编译时扩展到以下代码(注意额外的大括号以创建对象的有限作用域):
但是根据另一个MSDN页面,
通过使用finally块,可以清除在try块中分配的任何资源。
更新:如果变量可见性是原因,那么我们先声明变量并将其赋值为null,然后在try块中初始化它怎么样?这比原来的代码好吗?
{
T resource = null;//Now it is visible in the try block
try
{
resource =expression;// in case an exception is thrown here
embedded-statement
}
finally
{
if (resource != null)
((IDisposable)resource).Dispose();
}
}
发布于 2022-11-29 18:48:32
为什么在try块周围有一个额外的
{}
?
因为using
语句不等同于:
T resource = expression;
try
{
embedded-statement
}
finally
{
if (resource != null)
((IDisposable)resource).Dispose();
}
// resource is still in scope here!
resource
仍然在上述代码之后的作用域中,但在using
语句之后不应该在作用域中。
想象一下,如果resource
也是一个字段的名称,并且将Console.WriteLine(resource);
放在using
语句之后。通常,它会打印字段,但是如果using
语句被上面的代码所替换,那么您就会打印刚才分配的资源!
因此,为了确保语义保持不变,需要额外的{}
。
第一个语句不应该移到try块中吗?
不是的。using
语句设计为在资源初始化失败时抛出异常。
通过使用finally块,可以清除在try块中分配的任何资源。
是的,但这并不意味着您必须将所有资源都放在try
块中,也不意味着将资源放在try
块中是清理它们的唯一方法。
如果resource
的初始化引发异常,那么资源将不会被初始化,因此无论如何都没有资源要处理--在这种情况下,finally
块不需要运行,因此将该行置于try
之外是完全可以的。
发布于 2022-11-29 18:33:50
如果资源是在try-块中声明的,那么它在finally块中是不可见的,因为变量的作用域总是限制在实际块中(并且块嵌套得更深)。
周围的块将资源的范围限制在这个尝试-最终构造+声明。正如using语句中声明的资源的范围仅限于此using语句一样。
尝试-最后(C#参考)的文档说:
通过使用一个finally块,您可以清除在try块中分配的任何资源,并且即使在try块中发生异常,也可以运行代码。通常,当控件离开try语句时,最终块的语句就会运行。控制的转移可以发生在正常执行、中断、继续、goto或返回语句的执行,或者异常从try语句中传播的结果。
但让我们做个测试看看会发生什么。有了这门课
class Resource : IDisposable
{
public void Dispose()
{
Console.WriteLine("disposing");
}
public override string ToString() => "not null";
}
这种方法:
private static Resource Throw()
{
throw new NotImplementedException();
}
让我们编写这个测试:
Resource resource = new();
try {
using (resource = Throw()) {
Console.WriteLine("inside using block");
}
Console.WriteLine("after using block");
} catch {
Console.WriteLine($"catch: resource is {resource}");
}
我们的想法是在using语句之前分配一个资源,并在using语句的声明部分抛出一个异常。如果使用语句的最后隐藏部分被执行,那么应该打印disposing
。但是,相反,这是打印到控制台:
catch: resource is not null
这表明,如果在声明部分抛出异常,using
-statement实际上不会执行finally
-block。
如果构造函数抛出异常,那么无论如何也没有什么可处理的。
如果在返回之前抛出异常,那么工厂方法应该释放已经分配的任何资源,因为这不是调用者的责任。然后,该方法可以返回null
或抛出异常。
如果操作正确,那么资源是在try
部分之前创建的还是在try-finally
-statement的内部创建并不重要,因为如果出现异常,将没有什么可处理的。
当不需要释放任何东西时(因为可以用using-语句代替),通常使用try-finally语句,但是必须重置某些状态。例如,为长时间运行的操作设置等待游标。将长时间运行的操作包装在try块中,并在“最后”块中恢复正常游标,确保等待游标不会持久。
https://stackoverflow.com/questions/74618698
复制相似问题