返回一个Disposable对象以在using块中使用

时间:2010-11-18 22:02:03

标签: c# idisposable using-statement

如何在函数中返回一次性对象以确保它在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与此类似,以免混淆答案和讨论。

5 个答案:

答案 0 :(得分:4)

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

因此,当它从函数中出来时,从使用块中返回的对象已经被处理掉了。

请参阅以下代码以获取示例

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;
            }
        }
    }
}

这将产生以下输出:

enter image description here

答案 1 :(得分:3)

'使用'正在为你做try / catch工作,只需要db.Open;使用将保证无论它是否抛出,它都将处置你的连接。

答案 2 :(得分:2)

如果DBHandle实现了IDisposable,那么你应该有什么用。

没有'特殊'方式返回IDisposable。

答案 3 :(得分:2)

你有正确的方法,但似乎有点迷失它是怎么回事。

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

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也是如此 - 它不会在{{1并且using被强制转换为db,这意味着如果该赋值无效则无法编译,并且IDisposable可以隐式或显式实现,这将以任何方式工作。

当然,无论是否发生异常,Dispose()块都会执行。您无法使用finally,因为它等同于using,如果发生异常,您希望方法中的finally 。因此,你拿最后一个把它变成一个捕获:

Dispose()

除了添加空检查(也许你可以排除对它的需要)以及我使用裸DBHandle GetDB() { var db = DatabaseObj.GetHandle(); try { db.Open(); return db; } catch { if(db != null) ((IDisposable)db).Dispose(); throw; } } (这通常是一个好的)之外,这几乎和你一样想要在不改变或检查异常的情况下重新抛出异常。在某些情况下,抛出新异常会更好,在这种情况下,您应该将原始异常包含在该新异常的throw属性中,以便向调试的人提供进一步的信息。

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

答案 4 :(得分:1)

返回值只需实现IDisposable

实际上,这句话必须是真的:

IDisposable db = GetDB();

如果编译,您可以将GetDB()放在using声明中。