我们的代码库中有以下构造,用于确保在使用后处理特定资源:
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'"
但编译器拒绝编译该代码,因为它无法知道在ids
完成后总是会分配ExecuteWithFinish
(或抛出异常,这将阻止执行{{ 1}}无论如何)。
DoSomethingElseWith
的重载,它会从传入的ExecuteWithFinish
返回值,这很难看。Func
并覆盖其DisposableThing
方法来调用Dispose
,这比每次构建委托更简洁,更整洁,更快捷(这可能是我最终会做什么)。但是对于我自己的启发和“假设”的精神,是否有可能通知甚至欺骗编译器允许编写#4中的代码?
编辑:是的,我知道我可以写Finish
并完全绕过这个问题,但是(a)我不想执行不必要的任务(b)我想要尽可能少地改变代码。
答案 0 :(得分:5)
我会采取不同的方法。
我将假设出于某种原因,您必须始终在Finish()
之前调用Dispose()
方法,该方法也必须始终被调用。
这可能是一个轻率的假设,它确实提出了一个问题:为什么不将Finish()
的功能放入Dispose()
?然而...
首先,创建一个接口,用Finish()
方法封装一次性物品:
public interface IDisposableThingWithFinish : IDisposable
{
void Finish();
}
并更改您的DisposableThing
课程,以便它实现IDisposableThingWithFinish
。
然后你可以编写一个一次性类来封装调用Finish()
然后Dispose()
,如下所示:
public sealed class DisposingFinisher : IDisposable
{
readonly IDisposableThingWithFinish _item;
public Disposing(IDisposableThingWithFinish item)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
_item = item;
}
public void Dispose()
{
try
{
_item.Finish();
}
finally
{
_item.Dispose();
}
}
}
你可以这样使用Finisher
:
using (var disposableThing = new DisposingFinisher(DisposableThing.Begin()))
{
// Do something.
}
答案 1 :(得分:0)
简单的空赋值将避免编译器警告,如compiler error CS0165的文档中所述:
List<int> ids = null;