尝试/最终阻止与调用处置?
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 正在尝试在后台运行,尽管它已关闭并且应该被处置