我对 c# 有点陌生,更习惯于脚本语言。我喜欢“使用”的想法,你实例化一个对象,然后只要你需要它就在它的范围内操作,然后当它完成它的目的时让它自行处理。
但是,这对我来说并不自然。当人们向我展示使用它的示例时,我认为它是完成这项工作的好工具,但我从来没有想过在我自己的编程中用它来解决问题。
我如何识别使用的好地方using
以及如何将它与 try-catch 块结合使用。它们是进入块内部,还是您通常想在 try 块中包含 using 语句?
我对 c# 有点陌生,更习惯于脚本语言。我喜欢“使用”的想法,你实例化一个对象,然后只要你需要它就在它的范围内操作,然后当它完成它的目的时让它自行处理。
但是,这对我来说并不自然。当人们向我展示使用它的示例时,我认为它是完成这项工作的好工具,但我从来没有想过在我自己的编程中用它来解决问题。
我如何识别使用的好地方using
以及如何将它与 try-catch 块结合使用。它们是进入块内部,还是您通常想在 try 块中包含 using 语句?
using
只能与实现IDisposable
;的类型一起使用 它保证Dispose()
即使发生错误也会调用该方法。
这段代码:
using (MyDisposableType x = new MyDisposableType())
{
// use x
}
相当于:
MyDisposableType x = new MyDisposableType();
try
{
// use x
}
finally
{
x.Dispose();
}
我很少编写 try/catch 块 - 大多数异常都会被抛出(接近)堆栈顶部。如果我确实需要一个 try/catch 块,我不确定将它放在using
语句内部还是外部之间是否特别一致。这实际上取决于您是否希望在运行异常处理代码之前或之后处理资源。
如果您询问何时应该编写using
语句 - 任何时候您“拥有”一个实现IDisposable
(直接或间接通过继承)并控制其生命周期的对象。这通常是一个使用非托管资源(如文件句柄或网络连接)的对象。这并不总是非常明显,但你可以通过经验学习。几乎所有与 IO 相关的东西都是一次性的,Windows 句柄(用于字体等)也是类似的。
我曾经认为它是“每次当类型实现 IDisposable 并且你不再需要这个特定实例时使用它”。
如果您想知道如何在自己的设计中创造性地使用它,请查看您自己的一些代码,以了解在退出封闭块之前绝对必须执行特定代码位的情况。这些是try
/finally
或using
可以帮助您的情况。
特别是,如果你试图通过捕获所有异常来实现这一点,那么你真的必须改为try
/finally
或using
改为。
如果该模式多次出现,您可以创建一个实现类IDisposable
以捕获该模式,并允许您使用该using
语句调用该模式。但是,如果您有一个似乎是一次性的特定案例,那么只需使用try
/ finally
。
两者非常相似,实际上 -using
用try
/指定finally
,但即使我们只有using
,我们也可以自己构建try
/ finally
:
public class DisposeAnything : IDisposable
{
public Action Disposer;
public void Dispose()
{
Disposer();
}
}
现在你可以说:
using (new DisposeAnything
{
Disposer = () => File.Delete(tempFileName)
})
{
// blah
}
这与以下内容相同:
try
{
// blah
}
finally
{
File.Delete(tempFileName);
}
只需将其视为在离开作用域时执行某些代码的一种方式。
什么米奇说,加上..
您可以在 try..catch 块外部或内部使用 using 语句,这实际上取决于您要实现的目标,即您是否合理地期望在使用您计划从中恢复的特定对象时抛出异常,例如.
同样,如果需要,您还可以在 finally 块中处理实现 IDisposable 的对象。
当您需要确定性对象处理时使用using 。例如,如果您打开一个文件,该文件将被锁定。您通常希望尽快关闭文件,以便其他程序可以访问它。如果您不使用using和 write smth,例如:
System.IO.FileStream writeStream = new System.IO.FileStream( fileName, System.IO.FileMode.OpenOrCreate ) );
System.IO.BinaryWriter writer = new System.IO.BinaryWriter( writeStream ) );
//do smth
并且在“do smth”期间发生异常,您不知道在文件上操作的对象何时真正被释放并且文件被关闭。使用using你肯定知道,一旦你离开using语句块——直接或通过异常,通过调用 IDisposable::Dispose 来处理using语句中的对象:
using( System.IO.FileStream writeStream = new System.IO.FileStream( fileName, System.IO.FileMode.OpenOrCreate ) ) {
using( System.IO.BinaryWriter writer = new System.IO.BinaryWriter( writeStream ) ) {
//do stmth
}
}
您可以将 using 包含在 try/catch 块中,也可以将 try/catch 块包含在 using 中。
使用很好的一种情况是当您使用 DBConnection 和 DBCommands 进行数据库操作时:
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(sql, conn))
{
// do something here
}
现在,当您离开 using 块时,您的命令将被释放并关闭连接。
米奇说的是对的。因此 using 的主要用途是确保 IDisposable 对象得到处理,而无需编写 try/catch 或 try/finally 语句。
现在,还有一个更高级的用法,您可能也会觉得有趣。当您使用 using 语句时,编译器会生成一个 try/finally,并且它还会在它生成的 finally 内部为您生成对 Dispose() 的调用。您可以将此 Dispose() 方法用作“钩子”来做任何您想做的事情……不必与释放资源相关。
例如,Jeffrey Richter 在他编写的计时器对象中使用了它。你可以用它做这样的事情(仅限概念):
using(var x = new Timer())
{
// Do the operation you want timed
}
// Now, the timer has been stopped, because the
// compiler-generated call to Dispose() has already
// occurred, and Jeffrey uses Dispose() to stop
// the timer.
如果一个类实现了 IDisposable,这可能是有充分理由的。所以任何实现 IDisposable 的类都应该被释放。
鉴于它是所谓的“语法糖”,并且会产生与 try/finally dispose 构造相同的 IL,它实际上只是一种“简化”此类代码的好方法。
我喜欢使用它来简化大量使用一次性对象的代码部分,即访问文件和图形对象等资源,并且我想确保我不会忘记处理资源对象的处理。
我注意到,当已知 Dispose 方法会发出通知时,程序员不会费心调用它,因为它看起来毫无意义。但是,如果对象实现 IDisposable 它(希望)是有原因的,并且该对象的未来版本实际上可能在 Dispose 方法中有代码 - 所以总是调用它。