我们的代码库中有以下结构,用于确保特定资源在使用后被处理:
这是它使用的一个例子:
由于这种模式如此常见,我们在
这使我们可以将第二个样本重写为:
但编译器拒绝编译该代码,因为它无法知道在
using (var disposableThing = DisposableThing.Begin())
{
// do something
disposableThing.Finish(); // must always be called
}
这是它使用的一个例子:
List<int> ids;
using (var disposableThing = DisposableThing.Begin())
{
ids = disposableThing.GetSomeIds();
disposableThing.Finish();
}
DoSomethingElseWith(ids);
由于这种模式如此常见,我们在
DisposableThing
上编写了一个方法来封装它:static void ExecuteWithFinish(Action<DisposableThing> action)
{
using (var disposableThing = Begin())
{
action(disposableThing);
disposableThing.Finish();
}
}
这使我们可以将第二个样本重写为:
// #4
List<int> ids;
DisposableThing.ExecuteWithFinish(disposableThing =>
{
ids = disposableThing.GetSomeIds();
});
DoSomethingElseWith(ids); // compiler error "Use of unassigned local variable 'ids'"
但编译器拒绝编译该代码,因为它无法知道在
ExecuteWithFinish
完成后(或抛出异常,这将防止执行DoSomethingElseWith
)ids
是否总是被分配。
- 我知道我可以添加一个重载的
ExecuteWithFinish
,从传入的Func
返回值,但这很丑陋。 - 我知道我可以子类化
DisposableThing
并覆盖其Dispose
方法以调用Finish
,这比每次构造一个委托更干净、更整洁、更快速(这可能是我最终会做的)。
但出于我的自我启发和“如果”的精神,有没有可能通知甚至欺骗编译器允许#4中的代码按原样运行?
编辑:是的,我知道我可以写List<int> ids = null;
并完全规避这个问题,但(a)我不想执行不必要的赋值(b)我希望尽可能少地改变代码。
disposableThing.Finish()
的注释中写着“必须始终调用”。即使do something
抛出异常,这个说法是否仍然成立?如果是这样的话,你应该将整个Begin()
Finish()
Dispose()
转化为一个单独的类,并使用一个using
语句进行管理。 - Matthew WatsonIDisposable
模式,这似乎比使用Finish
方法重新发明机制更明显?将Finish
中的代码放入您的Dispose
方法中(或者仅从Dispose
调用Finish
)。这样,您就可以避免编译器错误,并获得语言支持,因为Finish
中的代码将在using语句结束时自动调用。 - Dirk Vollmarnull
不是一种不必要的赋值;它甚至不会在堆上创建实例,因此基本上是免费的。 - Sefe