我问了一个关于returning a Disposable (IDisposable
) object from a function的问题,但我认为如果我在那里提出这个问题,我会讨论这个问题。
我创建了一些示例代码:
class UsingTest
{
public class Disposable : IDisposable
{
public void Dispose()
{
var i = 0;
i++;
}
}
public static Disposable GetDisposable(bool error)
{
var obj = new Disposable();
if (error)
throw new Exception("Error!");
return obj;
}
}
我故意用这种方式编码,因为我打电话给:
using (var tmp = UsingTest.GetDisposable(true)) { }
使用调试器,我注意到Dispose
方法永远不会执行,即使我们已经实例化了Disposable
对象。如果我正确理解Dispose
的目的,如果这个类实际上已经打开了句柄之类的东西,那么我们就不会在完成它们后立即关闭它们。
我问这个问题是因为这种行为符合我的预期,但在相关问题的答案中,人们似乎表明using
会处理所有问题。
如果using
仍以某种方式处理所有这些问题,有人可以解释我所缺少的内容吗?但是,如果此代码确实可能导致资源泄漏,您会如何建议我编写代码GetDisposable
(条件是我必须实例化IDisposable
对象并运行可能在return语句之前抛出异常的代码)?
答案 0 :(得分:11)
它从未被调用的原因是因为你分配它的方式。永远不会分配“tmp”变量,因为GetDisposable(bool)
函数永远不会返回,因为你抛出异常。
如果您要改为说明以下内容,
using (var tmp = new Disposable())
{
throw new ArgumentException("Blah");
}
然后您会看到IDisposable::Dispose()
确实被调用。
要理解的基本要点是using
块必须获得对IDisposable
对象的有效引用。如果发生某些异常以致using
块中声明的变量未被分配,那么您运气不好,因为using
块将不知道IDisposable
对象。 / p>
至于从函数返回IDisposable
对象,你应该使用函数内部的标准catch
块来在发生故障时调用Dispose()
,但显然你应该不要使用using
块,因为这会在您准备好之前自行处理对象。
答案 1 :(得分:4)
根据您对GetDisposable
所需的语义,这可能是我实现它的方式:
public static Disposable GetDisposable(bool error)
{
var obj = new Disposable();
try
{
if (error)
throw new Exception("Error!");
return obj;
}
catch (Exception)
{
obj.Dispose();
throw;
}
}
答案 2 :(得分:3)
这是因为永远不会分配tmp变量。使用一次性物品需要注意的事项。 GewtDisposable的更好定义是:
public static Disposable GetDisposable(bool error)
{
var obj = new Disposable();
try
{
if (error)
throw new Exception("Error!");
return obj;
}
catch
{
obj.Dispose();
throw;
}
}
因为它确保了obj的处理。
答案 3 :(得分:1)
IDisposable接口只是保证实现它的类具有Dispose方法。它对调用此方法没有任何作用。退出块时,using块将调用对象上的Dispose。
答案 4 :(得分:1)
您在IDisposable
中创建GetDisposable
但由于您通过抛出异常退出该函数,因此永远不会返回该函数,因此永远不会分配tmp
。 using语句是
var tmp = UsingTest.GetDisposable(true);
try { }
finally
{
if(tmp != null) tmp.Dispose();
}
你永远不会到达try块。您的示例中的解决方案是在创建一次性obj之前检查error
标志:
public static Disposable GetDisposable(bool error)
{
if (error)
throw new Exception("Error!");
return new Disposable();
}
答案 5 :(得分:0)
一个相关的问题是Handling iDisposable in failed initializer or constructor,我认为答案是如果你想避免从失败的构造函数中泄漏一次性对象,你将不得不从构造函数中走私对象的副本(例如藏匿处)它在传入的容器中,或者将它分配给传递的引用变量)并将构造函数调用包装在catch块中。 Icky,但我不知道怎么做得更好。由于初始化程序的工作原理,VB.net实际上可以比C#更好地管理。