使用IDisposable检查约束 - 疯狂还是天才?

JSB*_*ոգչ 6 c# idisposable exception

我在今天工作的代码库中遇到了一个模式,最初看起来非常聪明,后来让我疯了,现在我想知道是否有办法拯救聪明的部分,同时尽量减少疯狂.

我们有一堆实现的对象IContractObject,以及一个如下所示的类InvariantChecker:

internal class InvariantChecker : IDisposable
{
    private IContractObject obj;

    public InvariantChecker(IContractObject obj)
    {
        this.obj = obj;
    }

    public void Dispose()
    {
        if (!obj.CheckInvariants())
        {
            throw new ContractViolatedException();
        }
    }
}

internal class Foo : IContractObject
{
    private int DoWork()
    {
        using (new InvariantChecker(this))
        {
            // do some stuff
        }
        // when the Dispose() method is called here, we'll throw if the work we
        // did invalidated our state somehow
    }
}
Run Code Online (Sandbox Code Playgroud)

这用于提供状态一致性的相对无痛的运行时验证.我没有写这个,但它最初似乎是一个非常酷的主意.

但是,如果Foo.DoWork引发异常,则会出现问题.抛出异常时,我们可能处于不一致状态,这意味着它InvariantChecker 也会抛出,隐藏原始异常.当异常传播到调用堆栈时,这可能会发生几次InvariantChecker,每个帧都会隐藏下面的帧中的异常.为了诊断问题,我不得不禁用抛出,InvariantChecker然后才能看到原始异常.

这显然是可怕的.但是,有没有什么方法可以拯救原始想法的聪明性而不会获得可怕的异常隐藏行为

Jon*_*eet 9

我不喜欢using以这种方式重载意义的想法.为什么不使用一个代理类型的静态方法呢?所以你写道:

InvariantChecker.Check(this, () =>
{
    // do some stuff
});
Run Code Online (Sandbox Code Playgroud)

或者甚至更好,只需将其作为扩展方法:

this.CheckInvariantActions(() =>
{
    // do some stuff
});
Run Code Online (Sandbox Code Playgroud)

(注意,需要"this"部分才能让C#编译器查找适用的扩展方法this.)如果需要,还可以使用"普通"方法实现操作,并使用方法组转换为其创建委托.如果您有时希望从正文返回,您可能还希望允许它返回一个值.

现在CheckInvariantActions可以使用类似的东西:

action();
if (!target.CheckInvariants())
{
    throw new ContractViolatedException();
}
Run Code Online (Sandbox Code Playgroud)

我还建议CheckInvariants应该直接抛出异常,而不是只返回bool- 这样异常可以提供有关违反哪个不变量的信息.


jas*_*son 5

这是对使用模式的可怕滥用.使用模式用于处理非托管资源,而不是像这样的"聪明"技巧.我建议只写直接的代码.


Tob*_*oby 0

向类添加一个属性InvariantChecker,允许您禁止检查/抛出。

internal class InvariantChecker : IDisposable
{
    private IContractObject obj;

    public InvariantChecker(IContractObject obj)
    {
        this.obj = obj;
    }

    public bool Suppress { get; set; }

    public void Dispose()
    {
        if (!this.Suppress)
        {
            if (!obj.CheckInvariants())
            {
                throw new ContractViolatedException();
            }
        }
    }
}

internal class Foo : IContractObject
{
    private int DoWork()
    {
        using (var checker = new InvariantChecker(this))
        {
            try
            {
                // do some stuff
            }
            catch
            {
                checker.Suppress = true;
                throw;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是可怕之上的可怕。 (2认同)