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
语句仍然能够正确地处理对象?(在我看来,它会创建eNullReferenceException
或其他东西。)我想我对你更一般的问题的回答很好地概括了这一点,你上面的代码对我来说看起来是正确的。