有没有办法告诉编译器将始终执行特定的代码块?

时间:2016-11-30 13:13:55

标签: c#

我们的代码库中有以下构造,用于确保在使用后处理特定资源:

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)我想要尽可能少地改变代码。

2 个答案:

答案 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;