【问题标题】:Return a Disposable object for use in a using block返回一个 Disposable 对象以在 using 块中使用
【发布时间】:2010-11-18 22:02:03
【问题描述】:

如何在我的函数中返回一次性对象以确保它在 using 块中正常工作?在我的函数中,我想对一次性对象采取行动并考虑错误,这使这变得复杂。

到目前为止,我有类似于以下代码的内容:

DBHandle GetDB()
{
/*  // I can't do this, because the using block would dispose of my object within this function
    using( var db = DatabaseObj.GetHandle() )
    {
        db.Open();
        return db;
    }
*/
    var db = DatabaseObj.GetHandle();
    try
    {
        db.Open();
        return db;
    }
    catch (Exception ex)
    {
        db.Dispose();
        throw ex;
    }
}

// In other code:
using( var obj = GetDB() ){ /* ... */ }

编辑:Posted a more general question 与此类似,以免混淆答案和讨论。

【问题讨论】:

  • 我认为我对您的更一般性问题的回答总结得很好,您上面的代码对我来说看起来是正确的。

标签: c# idisposable using-statement


【解决方案1】:

提示:从 using 块返回一次性对象时,请记住 在执行返回语句时完成对 Dispose() 的调用!!!

所以从 using-block 中返回的对象在从函数中出来时就已经被释放了。

示例见以下代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class MyDisposable : IDisposable
    {
        public void DoSomething()
        {
            Console.WriteLine("  In DoSomething");
        }

        #region IDisposable Members

        public void Dispose()
        {
            Console.WriteLine("  In Dispose");
        }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Main\n");

            Console.WriteLine("Before NormalMethod");
            NormalMethod();
            Console.WriteLine("After NormalMethod\n");

            Console.WriteLine("Before ReturningMethod");
            MyDisposable m = ReturningMethod();
            m.DoSomething(); // Here the object already has been disposed!
            Console.WriteLine("After ReturningMethod\n");

        }

        private static void NormalMethod()
        {
            using (MyDisposable myDisposable = new MyDisposable())
            {
                Console.WriteLine("  In NormalMethod");
            }
            return;
        }

        private static MyDisposable ReturningMethod()
        {
            using (MyDisposable myDisposable = new MyDisposable())
            {
                Console.WriteLine("  In ReturningMethod");
                return myDisposable;
            }
        }
    }
}

这将产生以下输出:

【讨论】:

    【解决方案2】:

    'Using' 正在为你做你的 try/catch 工作,只要有 db.Open; using 将保证无论是否抛出,它都会处理您的连接。

    【讨论】:

    • 对,但如果没有遇到任何异常,我不想在GetDB 函数中处理db。所以,我不能在GetDB 中使用using
    • 这不是“使用”的工作方式。相反,您需要一个工厂,只要它仍然有效,它就可以重用它的对象。
    • 实际上,try..finally 会比 try..catch 更好,因为它不处理异常,顺便说一句,这正是 using 块所做的。所以using 块已经完成了工作,而且效果更好。 :)
    • @palswim:GetDB 方法中不需要using,也不需要try..catch。只需获取连接,打开它并返回它。调用GetDBusing 块将始终释放对象。
    • @Guffa:我想我不知道try..catch 的内部运作方式。你是说,即使我的函数没有进入return 行,更高级别的using 语句仍然能够正确处理对象? (在我看来,它会创建 e NullReferenceException 或其他东西。)
    【解决方案3】:

    您的方法是正确的,但似乎有点迷失方向。

    考虑一下你(正确地)说不能工作的代码:

    DBHandle GetDB()
    {
        using( var db = DatabaseObj.GetHandle() )
        {
            db.Open();
            return db;
        }
    }
    

    这段代码几乎等同于:

    DBHandle GetDB()
    {
        var db = DatabaseObj.GetHandle();
        try
        {
          db.Open();
          return db;
        }
        finally
        {
            if(db != null)//not included if db is a value-type
              ((IDisposable)db).Dispose();
        }
    }
    

    这里需要注意的一些事情包括,直到分配之后才会发生尝试(using 也是如此 - 它不会使您免受 using 分配之前的异常)和db 被强制转换为 IDisposable,这意味着如果该赋值无效,它就无法编译,而且 Dispose() 可以隐式或显式实现,这两种方式都可以。

    当然,无论是否发生异常,finally 块都会执行。您不能使用using,因为它等同于finally,并且您希望在发生异常时在您的方法中使用Dispose()。因此,您采用 finally 并将其变成一个 catch:

    DBHandle GetDB()
    {
        var db = DatabaseObj.GetHandle();
        try
        {
          db.Open();
          return db;
        }
        catch
        {
            if(db != null)
              ((IDisposable)db).Dispose();
            throw;
        }
    }
    

    这与您的几乎相同,除了添加了一个空检查(也许您可以排除对它的需要)并且我使用的是裸throw(这通常是一个好主意您将重新抛出异常而不更改或检查它。在某些情况下,抛出一个新异常会更好,在这种情况下,您应该将原始异常包含为该新异常的InnerException 属性,以便提供更多信息给调试的人)。

    总而言之,你走在了正确的轨道上。希望我已经帮助解释了原因。

    【讨论】:

      【解决方案4】:

      如果 DBHandle 实现了 IDisposable,那么你所拥有的应该可以工作。

      没有返回 IDisposable 的“特殊”方式。

      【讨论】:

        【解决方案5】:

        返回值只需实现IDisposable

        实际上,这句话一定是真的:

        IDisposable db = GetDB();
        

        如果编译成功,您可以将 GetDB() 放入 using 语句中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-14
          • 2012-09-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-23
          • 2017-10-04
          相关资源
          最近更新 更多