C# 返回一次性对象以在使用块中使用

C# 返回一次性对象以在使用块中使用,c#,idisposable,using-statement,C#,Idisposable,Using Statement,如何在函数中返回一次性对象,以确保它在使用块的中正常工作?在我的函数中,我希望对一次性对象进行操作,并对错误进行解释,这使问题变得复杂 到目前为止,我有一些类似于以下代码的代码: DBHandle GetDB() { /* // I can't do this, because the using block would dispose of my object within this function using( var db = DatabaseObj.GetHandle() )

如何在函数中返回一次性对象,以确保它在使用块的
中正常工作?在我的函数中,我希望对一次性对象进行操作,并对错误进行解释,这使问题变得复杂

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

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() ){ /* ... */ }

编辑:与此类似,以免混淆答案和讨论。

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


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

返回值只需实现
IDisposable

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

IDisposable db = GetDB();

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

using'正在为您执行try/catch工作,只需打开db;使用将保证无论它是否抛出,它都会处理您的连接。

您已经找到了正确的方法,但对于它的正确性似乎有点迷茫

考虑一下您(正确地)说无法工作的代码:

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();
    }
}
这里需要注意的几点包括,在赋值之后才进行尝试(同样的情况也适用于
使用
-它不会在
使用
中的赋值之前将您从异常中拯救出来),并且
db
被强制转换为
IDisposable
,这意味着如果该赋值无效,它将无法编译,而且,
Dispose()
可以隐式或显式实现,这两种方式都可以

当然,现在无论是否发生异常,
最终
块都将执行。您不能使用
using
,因为它相当于
finally
,并且您希望在方法中
Dispose()
仅在发生异常时使用。因此,您可以使用finally并将其转换为catch:

DBHandle GetDB()
{
    var db = DatabaseObj.GetHandle();
    try
    {
      db.Open();
      return db;
    }
    catch
    {
        if(db != null)
          ((IDisposable)db).Dispose();
        throw;
    }
}
这与您所做的几乎相同,只是添加了一个空检查(也许您可以排除它的需要),并且我使用的是裸
抛出
(通常,在不更改或检查异常的情况下重新引发异常是一个好主意。在某些情况下,引发新异常更好,在这种情况下,您应该将原始异常作为该新异常的
InnerException
属性,以便向调试人员提供更多信息)


总而言之,你的思路是正确的。希望我已经解释了原因。

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

因此,从using块中返回的对象在退出函数时将被释放。

有关示例,请参见以下代码

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;
            }
        }
    }
}
这将产生以下输出:


是的,但是如果
GetDB
函数没有遇到任何异常,我不想在
GetDB
函数中处理
db
。因此,我不能在
GetDB
中使用
using
。这不是“using”的工作方式。相反,你想要的是一个工厂,只要它仍然有效,就可以重用它的对象。事实上,
尝试..最后,
这比一个
try..catch
要好,因为没有处理异常,顺便说一句,这正是
using
块所做的。因此
using
块已经完成了工作,而且更好。@palswim:您不需要在
GetDB
方法中使用
或者
try..catch。只需获取连接,打开并返回即可。调用
GetDB
using
块总是会处理对象。@Guffa:我想我不知道
try..catch的内部工作原理。您的意思是,即使我的函数没有到达
return
行,更高级别的
using
语句仍然能够正确地处理对象?(在我看来,它会创建e
NullReferenceException
或其他东西。)我想我对你更一般的问题的回答很好地概括了这一点,你上面的代码对我来说看起来是正确的。