使用IDisposable来管理引用计数

本文关键字:引用 管理 IDisposable 使用 | 更新日期: 2023-09-27 18:21:43

背景:

与这个问题类似,我希望将IDisposable用于其他目的。

目标:

这方面的应用程序并不十分相关,但只是一个很好的例子:我有一个Windows窗体应用程序,在那里我实现了Undo设计模式。通常,这是通过从UI元素中截取"Value Changed"类事件来完成的。对于DataGridViewCellEndEdit等等。然而,在某些情况下,我会以编程方式更改数据,并且我希望在不跟踪它们的情况下执行每个撤消操作跟踪的事情。

到目前为止:

我有办法做到这一切,但我在计数上管理我的"我应该撤消吗"逻辑:

private int _undoOverrides = 0;
public bool ShouldUndo { get { return _undoOverrides < 1; } }
public void DoNotUndo() { ++_undoOverrides; }
public void ResumeUndo() { --_undoOverrides; }

现在,这工作得很好,但您必须记住在以DoNotUndo()开始的业务逻辑结束时调用ResumeUndo()。我想:

也许我不是一个白痴,不会把它搞砸,但如果我不得不在我传递的代码中公开这个接口呢?如果可能的话,我希望编译器能帮我处理这个问题。

想法:

为此,我正在考虑使用一个实现IDisposable的类。这样,我的代码的用户就可以使用using块,而不用担心家务。这就是我目前所拥有的:

private static int _refCount = 0;
public static int ReferenceCount { get { return _refCount; } }
class HallPass : IDisposable
{
    protected bool bActive;
    public HallPass()
    {
        ++Program._refCount;
        bActive = true;
        Console.WriteLine("Acquired hallpass!");
    }
    public void Dispose()
    {
        if (bActive)
            --Program._refCount;
        bActive = false;
        Console.WriteLine("Hallpass expired!");
    }
}

我包含了一个布尔值,这样我就可以确信我不会在Dispose()上重复倒数。因此,使用HallPass所要做的就是:

using (new HallPass())
{
    // do things...
}

问题是:

这是个好主意吗?为什么这可能是个坏主意?有什么我应该知道的吗?

此外,我觉得这很愚蠢,但我很确定引用计数不是合适的术语。它就像引用计数,但没有可管理的引用或可释放的内存编辑:可能,只是现在不合适。

这就像一个互斥体或一个关键部分,因为你试图在一段代码中对规则进行异常(另一个用词不当的词,因为我指的不是throw),但这两者都不是,因为它们在一个范围内是互斥的——如果你愿意的话,这是以嵌套的方式完成的。这就是为什么它是一个计数,而不是布尔值。

使用IDisposable来管理引用计数

我首先关心的是Program._refCount可以从多个线程访问,并且它没有被同步。但是您可能会争辩说您的应用程序是单线程的。

下一个更大的问题是,你并没有按照应该使用的方式真正使用一次性模式。我个人认为,以应该使用的方式使用模式很重要,尤其是当你不是唯一一个处理代码的人时。

  1. 现在,您需要记住必须呼叫Dispose(),而不是记住呼叫ResumeUndo()。问题是:当您的团队成员想要使用HallPass时,他们是否会自然而然地意识到需要呼叫Dispose ()?(using语句很好,但不能在所有场景中都使用。如果有一个HallPass的寿命超过了单个方法的范围,则不能将其封装在using语句中)
  2. 尽管忘记在IDisposible上调用Dispose()是不好的,但它通常不会影响程序的正确性——是的,您的程序会出现性能问题、泄漏等,但从功能上讲,它通常仍然是正确的。然而,对于您的HallPass,如果有人忘记调用Dispose(),我想会有一个功能错误。而且这个bug很难追踪