首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >编译器将“使用”语句翻译为“最后尝试”的C#语句

编译器将“使用”语句翻译为“最后尝试”的C#语句
EN

Stack Overflow用户
提问于 2022-11-29 18:29:23
回答 2查看 77关注 0票数 2

MSDN这个被接受的答案说,

代码语言:javascript
运行
复制
using (T resource = expression)
    embedded-statement

编译器将其翻译为以下代码:

代码语言:javascript
运行
复制
{
    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块中初始化它怎么样?这比原来的代码好吗?

代码语言:javascript
运行
复制
{
    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();
    }
}
EN

回答 2

Stack Overflow用户

发布于 2022-11-29 18:48:32

为什么在try块周围有一个额外的{}

因为using语句不等同于:

代码语言:javascript
运行
复制
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之外是完全可以的。

票数 7
EN

Stack Overflow用户

发布于 2022-11-29 18:33:50

如果资源是在try-块中声明的,那么它在finally块中是不可见的,因为变量的作用域总是限制在实际块中(并且块嵌套得更深)。

周围的块将资源的范围限制在这个尝试-最终构造+声明。正如using语句中声明的资源的范围仅限于此using语句一样。

尝试-最后(C#参考)的文档说:

通过使用一个finally块,您可以清除在try块中分配的任何资源,并且即使在try块中发生异常,也可以运行代码。通常,当控件离开try语句时,最终块的语句就会运行。控制的转移可以发生在正常执行、中断、继续、goto或返回语句的执行,或者异常从try语句中传播的结果。

但让我们做个测试看看会发生什么。有了这门课

代码语言:javascript
运行
复制
class Resource : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("disposing");
    }

    public override string ToString() => "not null";
}

这种方法:

代码语言:javascript
运行
复制
private static Resource Throw()
{
    throw new NotImplementedException();
}

让我们编写这个测试:

代码语言:javascript
运行
复制
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。但是,相反,这是打印到控制台:

代码语言:javascript
运行
复制
catch: resource is not null

这表明,如果在声明部分抛出异常,using-statement实际上不会执行finally-block。

如果构造函数抛出异常,那么无论如何也没有什么可处理的。

如果在返回之前抛出异常,那么工厂方法应该释放已经分配的任何资源,因为这不是调用者的责任。然后,该方法可以返回null或抛出异常。

如果操作正确,那么资源是在try部分之前创建的还是在try-finally-statement的内部创建并不重要,因为如果出现异常,将没有什么可处理的。

当不需要释放任何东西时(因为可以用using-语句代替),通常使用try-finally语句,但是必须重置某些状态。例如,为长时间运行的操作设置等待游标。将长时间运行的操作包装在try块中,并在“最后”块中恢复正常游标,确保等待游标不会持久。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74618698

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档