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

pal*_*wim 2 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() ){ /* ... */ }
Run Code Online (Sandbox Code Playgroud)

编辑:发布了一个与此类似的更一般的问题,以免混淆答案和讨论。

Kna*_*bax 8

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

因此,从 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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将产生以下输出:

在此处输入图片说明


Jon*_*nna 3

你的方法是正确的,但似乎有点不知道如何才是正确的。

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

DBHandle GetDB()
{
    using( var db = DatabaseObj.GetHandle() )
    {
        db.Open();
        return db;
    }
}
Run Code Online (Sandbox Code Playgroud)

这段代码几乎相当于:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意的一些事情包括 try 直到赋值之后才会发生(同样的情况using- 它不会在 中的赋值之前使您免于异常using)并且这db意味着IDisposable它可以如果该分配无效,则不会编译,并且Dispose()可以隐式或显式实现,并且这两种方式都可以工作。

当然,现在finally无论是否发生异常,块都会执行。您不能使用它using,因为它相当于 afinally并且您只想在发生异常时才Dispose()在您的方法中使用它。因此,你把finally变成了一个catch:

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

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

总而言之,您走在正确的道路上。希望我能帮助解释原因。