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

发布于 2024-10-03 23:10:16 字数 751 浏览 0 评论 0原文

如何在函数中返回一次性对象以确保它在 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() ){ /* ... */ }

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

How do I return a disposable object in my function to ensure that it works properly within a using block? In my function, I want to act on the disposable object and also account for errors, which complicates this.

I have something similar to the following code so far:

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

Edit: Posted a more general question similar to this so as to not confuse answers and discussion.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

倾`听者〃 2024-10-10 23:10:16

提示:当从使用块返回一次性对象时,请记住
在执行 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;
            }
        }
    }
}

这将产生以下输出:

在此处输入图像描述

Hint: When returning a disposable object from your using-block, have in mind that
the call to Dispose() is done when the return-statement is executed!!!

So an object returned from within a using-block will already be disposed when it comes out of the function.

See the following code for an example

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

This will produce the following output:

enter image description here

我是男神闪亮亮 2024-10-10 23:10:16

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

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

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

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

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

You've got the right approach, but seem a bit lost as to how it's right.

Consider the code that you (correctly) say can't work:

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

This code is pretty much equivalent to:

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

A few things of note here include that the try doesn't happen until after the assignment (the same is true of using - it doesn't save you from exceptions prior to the assignment in the using) and that db is cast to IDisposable meaning both that it can't compile if that assignment isn't valid, and also that Dispose() can be either implicitly or explicitly implemented and this will work either way.

Now of course, finally blocks will execute whether an exception occurs or not. You can't use using because it's equivalent to a finally and you want to Dispose() in your method only if an exception occurs. Hence you take the finally and turn it into a catch:

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

This is pretty much the same as you have, except for the addition of a null check (maybe you can rule out the need for it) and that I'm using the bare throw (it's generally a good idea when you are going to re-throw an exception without altering or examining it. In some cases throwing a new exception is better, in which case you should include the original as the InnerException property of that new exception, so as to provide further information to someone debugging).

So all in all, you were on the right track. Hopefully I've helped explain why.

乄_柒ぐ汐 2024-10-10 23:10:16

“使用”正在为你做你的 try/catch 工作,只需 db.Open; using 将保证无论它是否抛出,它都会释放你的连接。

'Using' is doing your try/catch work for you, just have the db.Open; using will guarantee that regardless of whether it throws, it will dispose your connection.

瑾夏年华 2024-10-10 23:10:16

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

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

If DBHandle implements IDisposable then what you have should work.

There is no 'special' way of returning IDisposable.

蓝天 2024-10-10 23:10:16

返回值只需实现IDisposable

实际上,此语句必须为真:

IDisposable db = GetDB();

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

The return value simply has to implement IDisposable.

In practical terms, this statement must be true:

IDisposable db = GetDB();

If that compiles, you can put GetDB() in a using statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文