【问题标题】:How do I know the best place to use 'using'?我怎么知道使用“使用”的最佳位置?
【发布时间】:2009-04-03 07:06:19
【问题描述】:

我对 c# 有点陌生,更习惯于脚本语言。我喜欢“使用”的想法,你实例化一个对象,然后你在它的范围内操作,只要你需要它,然后当它完成它的目的时你让它自己处理。

但是,这对我来说并不自然。当人们向我展示使用它的示例时,我认为它是完成这项工作的好工具,但我从来没有想过在我自己的编程中用它来解决问题。

如何识别使用using 的好地方以及如何将它与try-catch 块结合使用。它们是进入块内部,还是您通常想在 try 块中包含 using 语句?

【问题讨论】:

    标签: c# using-statement


    【解决方案1】:

    using 只能与实现IDisposable 的类型一起使用;它保证即使发生错误也会调用Dispose()方法。

    这段代码:

    using (MyDisposableType x = new MyDisposableType())
    {  
        // use x
    }
    

    等价于:

    MyDisposableType x = new MyDisposableType();
    try
    {  
        // use x
    }
    finally 
    { 
        x.Dispose(); 
    }
    

    【讨论】:

    • 那么...在任何实现 IDisposable 的对象上使用它?总是?
    • 那么异常处理呢?
    • 请注意,永远不要将事务逻辑放入 finally,因为它不能绝对保证运行。直到我们至少获得事务性内存。例如。如果你拔掉电源插头最后就不能运行了!
    • 那你会怎么做呢?防止代码在断电时执行某些操作:D
    • @Jonathan Parker - 没有比“finally”更好的放置事务逻辑的地方了。
    【解决方案2】:

    我很少编写 try/catch 块 - 大多数异常会被抛出(接近)堆栈顶部。如果我确实需要一个 try/catch 块,我不确定将它放在 using 语句内部还是外部之间是否特别一致。这实际上取决于您是否希望在运行异常处理代码之前或之后释放资源。

    如果您询问何时,您应该编写using 语句 - 任何时候您“拥有”一个实现IDisposable 的对象(直接或间接通过继承)并控制它寿命。这通常是一个使用非托管资源(如文件句柄或网络连接)的对象。这并不总是非常明显,但您可以通过经验学习。几乎任何与 IO 相关的都是一次性的,Windows 句柄(用于字体等)也是类似的。

    【讨论】:

    • 阐明直接或通过继承实现 IDisposable 的对象(因为 danieltalsky “对 C# 来说有点新”)。
    • 嗯,我所说的“对 C# 有点陌生”是指“用 C# 编写商业代码不到 8 个月”。例如,我已经熟悉我在问题中提到的“使用”与“IDisposable”之间的联系。这是我缺少的“终身控制”部分。
    【解决方案3】:

    我曾经认为它是“每次当类型实现 IDisposable 并且你不再需要这个特定实例时使用它”。

    【讨论】:

      【解决方案4】:

      如果您想知道如何在自己的设计中创造性地使用它,请查看您自己的一些代码,以了解在退出封闭块之前绝对必须执行特定代码位的情况。这些是try/finallyusing 可以帮助您的情况。

      特别是,如果您尝试通过捕获所有异常来实现此目的,那么您确实必须改为 try/finallyusing

      如果该模式多次出现,您可以创建一个实现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);
      }
      

      只需将其视为在离开作用域时执行某些代码的一种方式。

      【讨论】:

      • 嘿,对 dispose 逻辑进行参数化是一个有趣的想法。我想有很多方法可以做到这一点。酷。
      • 采取上述极端,没有太多意义!但是,是的,可能有很多中途的情况。你可能会觉得这个讨论很有趣:incrediblejourneysintotheknown.blogspot.com/2009/02/…
      【解决方案5】:

      什么米奇说,加上..

      您可以在 try..catch 块外部或内部使用 using 语句,这实际上取决于您要实现的目标,即您是否合理地期望在使用您计划从中恢复的特定对象时抛出异常,例如。

      同样,如果需要,您还可以在 finally 块中 Dispose 实现 IDisposable 的对象。

      【讨论】:

        【解决方案6】:

        您可以将 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 块时,您的命令将被释放并关闭连接。

        【讨论】:

          【解决方案7】:

          米奇说的是对的。因此 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.
          

          【讨论】:

          • 似乎有点棘手。在右大括号之后,您无法访问 x。那么如何利用停止的计时器呢?
          • 是的,这是真的。对于 Richter 的计时器,他将经过的时间(以及已发生的垃圾回收次数)输出到控制台。所以他没有其他需要在右大括号之后引用该对象。
          • 但是,如果您愿意,也有一些方法可以实现这一目标。可以告诉对象触发事件或将结果放入集合中。或者,您可以使用 lambda 操作参数化对象以在其内部执行。但这离最初的问题太远了。
          【解决方案8】:

          如果一个类实现了 IDisposable,这可能是有充分理由的。所以任何实现 IDisposable 的类都应该被释放。

          【讨论】:

            【解决方案9】:

            鉴于它是所谓的“语法糖”,并且会产生与 try/finally dispose 构造相同的 IL,它实际上只是一种“简化”此类代码的好方法。

            我喜欢使用它来简化大量使用一次性对象的代码部分,即访问文件和图形对象等资源,并且我想确保我不会忘记处理资源对象的处置.

            【讨论】:

              【解决方案10】:

              当您需要确定性对象处置时,请使用 using。例如,如果您打开一个文件,该文件将被锁定。您通常希望尽快关闭该文件,以便其他程序可以访问它。如果你不使用 using 并像这样写:

              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 语句块——直接或通过异常 useing 语句中的对象是通过调用 IDisposable::Dispose: 来处理:

              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。"在我看来,如果一个对象实现了 IDisposable,如果不再需要该对象,则应该始终将其处置。通过实现 IDisposable,对象会说“我(可能)使用需要处置的资源”。
              • 如果相关对象的实现发生变化并立即处置变得至关重要怎么办?使用后不立即处理有什么好处?
              【解决方案11】:

              我注意到,当已知 Dispose 方法会发出通知时,程序员就不会费心调用它,因为它看起来毫无意义。但是,如果对象实现 IDisposable 它(希望)是有原因的,并且该对象的未来版本实际上可能在 Dispose 方法中有代码 - 所以总是调用它。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-05-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-02-05
                相关资源
                最近更新 更多