【发布时间】:2011-02-21 17:51:55
【问题描述】:
我问了我的朋友这个问题,他说它是用来销毁在异常处理过程中创建的对象。但是在c#GC中是用来销毁这类未使用的对象的,那么finally块的实际用途是什么。告诉我一个与此相关的场景。
【问题讨论】:
-
好吧,一方面,因为 .NET 仍然需要使用非托管资源来完成一些任务......
标签: c# memory-management idisposable
我问了我的朋友这个问题,他说它是用来销毁在异常处理过程中创建的对象。但是在c#GC中是用来销毁这类未使用的对象的,那么finally块的实际用途是什么。告诉我一个与此相关的场景。
【问题讨论】:
标签: c# memory-management idisposable
是一个无论是否发生异常都保证运行的块。
因此,通常情况下,如果您想要确保正确释放某种资源,您会使用它。与 during the exception handling 创建的对象无关。但是假设您有某种与数据库或文件句柄的连接。
如果是实现IDisposable 的托管对象,更好的方法通常是using 关键字。
【讨论】:
当它们不再被引用时,GC 将清除 托管 资源(您的应用程序在内存中创建的对象)。这不包括文件句柄、网络资源、数据库连接等...您必须在 finally 块中自行清除它们,否则可能无法清除它们(尽管大多数最终会清除) .
或,更常见的是:
许多要处理的非托管资源的类都实现了IDisposable 接口。这意味着您可以将代码包装在 using 块中,并确保将清除非托管资源(它会在超出范围时调用对象的 Dispose() 方法)。
使用 finally 块的一个很好的例子是使用 Office 互操作库。假设您打开 Microsoft Word,运行一些代码,代码失败...Word 将总是 需要关闭,无论是否发生错误。因此我会将结束代码放在finally 块中。
【讨论】:
GC 将最终销毁对象,这是真的。但是,当您拥有最好尽快释放的非内存资源时,finally 块很有用。例如,如果你有一个数据库连接,你不希望连接在终结器运行之前保持打开(可能是无限制的)很长时间,所以你把它放在一个 using 块中(这只是语法糖try ... finally 块):
using(var conn = new SqlConnection(...))
{
// use the connection
}
这是(基本上)的语法糖:
var conn = new SqlConnection(...);
try
{
// use the connection
}
finally
{
conn.Dispose();
}
现在,如果您没有 finally/using,那么当对象的终结器运行时,连接最终会被释放,但因为您不知道那会发生什么发生这种情况时,最好将对象的使用情况包装在 using 块中,以确保不再需要连接时立即关闭。
【讨论】:
finally 用于保证某些操作无论是否发生异常都能执行。
其中一个用途——尽管不是很“优雅”——是在先前打开的数据库连接上调用 close()。更一般地用于 GC 未清除的所有事物(例如 db 或网络连接)。
http://msdn.microsoft.com/en-en/library/zwc8s4fz(VS.80).aspx
【讨论】:
finally 块被使用,当你想要一段代码运行时不管是否发生异常。例如关闭数据库连接。
【讨论】:
finally 块不仅仅用于内存释放! 想想这个案例: 在“尝试”中阻止您- 1.打开与数据库的连接 2. 执行查询 第 2 步失败并出现异常。 但是,在任何情况下,您都应该关闭与数据库的连接 - 执行此操作的正确位置是在 finally 块内。
这只是一个例子。有许多不同的情况可能需要 finally 块。
【讨论】:
垃圾收集器将在适当的时候收集托管资源。对于那些还持有非托管资源(如网络资源、数据库连接或文件句柄等)的人,它们通常支持IDisposable 接口,如果操作正确,仍将通过终结器释放资源;但是通过Dispose()ing 在finally 块中,您可以控制何时这些资源被释放。
如果您只有一个 IDisposable 对象,那么 using 块可能更合适,尽管如果您 (a) 需要处理块内抛出的异常,或者 (b)有几个 IDisposable 对象一起使用(例如,SqlConnection、SqlCommand...)
【讨论】:
除了提到的其他场景之外,另一种常见的情况发生在锁上。 synclock 语句调用Monitor.Enter 并执行一段代码;然后它使用finally 块调用Monitor.Exit。如果代码在没有调用Monitor.Exit 的情况下离开块,那么任何其他使用锁保护的资源的尝试都将被永远阻止。
顺便说一句,即使synclock 内部发生了一些不好的事情,以至于被锁保护的资源应该被认为是损坏的并且没有被使用,保持锁被持有也不是处理这种情况的好方法。更好的方法是让同步锁中的代码显式地使对象无效,这样任何其他等待锁的代码都将被授予访问权限,然后在尝试使用无效对象时最终抛出异常。
【讨论】: