尝试/最终阻止与调用处置?

Posted

技术标签:

【中文标题】尝试/最终阻止与调用处置?【英文标题】:Try/Finally block vs calling dispose? 【发布时间】:2014-01-09 07:24:18 【问题描述】:

这两个代码示例有什么区别吗?如果没有,为什么using 存在?

StreamWriter writer;
try 
    writer = new StreamWriter(...)
    writer.blahblah();

 finally 
    writer.Dispose();

对比:

using (Streamwriter writer = new Streamwriter(...)) 
    writer.blahblah

我的意思是,在第二个示例中,您确实应该将它放在 try 块中,因此添加 finally 块确实不会花费更多的精力。我知道整个事情可能包含在一个更大的 try 块中,但是是的,这对我来说似乎是多余的。

【问题讨论】:

“你真的应该把它放在 try 块中” 为什么?您可以允许任何异常冒泡到堆栈顶部,并在日志中留下清晰的消息和堆栈跟踪。 foreach 也是如此,它只是调用迭代器方法和属性的更简洁的版本。 您的 try/finally 样本有缺陷,using() 块将正常工作。 我一直听说在 Dispose 方法执行期间抛出异常时,Using 语句似乎与Try-Finally 块相比没有用。我还看到 this MSDN 文章对 WCF 客户端说同样的话。 【参考方案1】:

您的代码存在一些问题。请永远不要这样写(改为使用),这就是原因

StreamWriter writer;
try 
    // What if you failed here to create StreamWriter? 
    // E.g. you haven't got permissions, the path is wrong etc. 
    // In this case "writer" will point to trash and
    // The "finally" section will be executed
    writer = new StreamWriter(...) 
    writer.blahblah();
 finally 
    // If you failed to execute the StreamWriter's constructor
    // "writer" points to trash and you'll probably crash with Access Violation
    // Moreover, this Access Violation will be an unstable error!
    writer.Dispose(); 

当你这样输入using

  using (StreamWriter writer = new StreamWriter(...)) 
    writer.blahblah();
  

等于代码

StreamWriter writer = null; // <- note the assignment

try 
  writer = new StreamWriter(...); 
  writer.blahblah();

finally 
  if (!Object.ReferenceEquals(null, writer)) // <- ... And to the check
    writer.Dispose();

【讨论】:

为什么使用 Object.ReferenceEquals(null) 而不仅仅是 !=null ?这里回答:***.com/a/51795257/2377343 @T.Todua 为什么是!=null 而不仅仅是writer?.Dispose()?我的问题的答案和你的一样:语言在进化。尤其是德米特里写下他的答案已经超过 6.5 年了 ;-)【参考方案2】:

这两个代码示例有什么区别

是的,using 在调用 Dispose 之前检查 null(即,它被扩展为引入空检查的实际代码)。

为什么使用存在?

因为代码更简洁。只是一个语法糖。

【讨论】:

【参考方案3】:

你所写的几乎就是using 总结的模式。因此,using 的意义在于避免每次使用 Disposable 对象时都必须编写相同的 try....finally 块。

至于您编辑的问题

[...] 在第二个示例中,您确实应该将其放在 try 块中,因此添加 finally 块确实不会花费更多的精力

您可能无法(或不想)显式处理来自blahblah 的错误,而您只是希望它冒泡到调用代码......但仍然在清理您的 StreamWriter 资源!

所以你最终会得到这个:

StreamWriter writer;
try
    writer = new StreamWriter(...)
    writer.blahblah();
catch
  throw; // pointless!
 finally [
    writer.Dispose();

【讨论】:

所以你最终会得到这个 --> 你为什么要那样做? finally 不会吞下异常,因此如果您只想将异常冒泡到调用代码,则根本不需要显式抛出它。 OP 说他们无论如何都会放一个try..catch,这只是一个人为的例子,说明为什么你可能不需要。 如果catch 块记录了发生的异常,并且finally 块根据原始文件是否正确处理Dispose 相关问题,则后一种模式可能没有用。 try 块已正常退出或通过异常退出。【参考方案4】:

后者只是前者的语法糖。他们应该做同样的事情,但后者需要更少的样板代码。

我建议使用using 一个,因为它不太可能出错。

【讨论】:

不幸的是,后者不仅仅是前者的语法糖。有一个微妙的区别:如果在前者中,StreamWriter 构造函数中抛出异常,代码将因浮动 AccessViolation 而崩溃,而稍后(即“使用...”)将正确运行【参考方案5】:

它们并不完全相同。 try/finally 块不能防止愚蠢的错误。

StreamWriter writer = new StreamWriter(...);
try 
  ...
  writer = new StreamWriter(...);
  ...
 finally 
  writer.Dispose();

请注意,只有第二个写入器会被处理掉。相比之下,

using (StreamWriter writer = new StreamWriter(...)) 
  ...
  writer = new StreamWriter(...);
  ...

将给出编译时错误。

【讨论】:

【参考方案6】:

首先,使用using 比您的代码更安全 - 它可以正确处理Disposable 对象构造中的错误,并且不会对空对象调用 dispose。

第二个区别在于代码的可读性 - 看看你的例子。第一个版本需要 7 行。第二个 - 只有 3 个。

【讨论】:

不幸的是,真正的区别不仅仅在于代码的可读性。如果 StreamWriter 构造函数中抛出异常,“try catch ”将因浮动访问冲突而崩溃;而“使用 ”会正确执行。 这就是第一段的内容。但我会重写它,因为它显然不清楚。

以上是关于尝试/最终阻止与调用处置?的主要内容,如果未能解决你的问题,请参考以下文章

WKWebView 正在尝试在后台运行,尽管它已关闭并且应该被处置

处置WPF用户控件

为啥代码会主动尝试阻止尾调用优化?

当我尝试使用 Axios 调用 Instagram API 时,我“被 CORS 政策阻止” [重复]

检查物品是不是已被处置的正确方法

尝试在 Web API Core 中使用 EF Core 在启动时更新数据库时出现“'无法访问已处置的对象”错误