使用IDisposable来管理引用计数
本文关键字:引用 管理 IDisposable 使用 | 更新日期: 2023-09-27 18:21:43
背景:
与这个问题类似,我希望将IDisposable用于其他目的。
目标:
这方面的应用程序并不十分相关,但只是一个很好的例子:我有一个Windows窗体应用程序,在那里我实现了Undo设计模式。通常,这是通过从UI元素中截取"Value Changed"类事件来完成的。对于DataGridView
、CellEndEdit
等等。然而,在某些情况下,我会以编程方式更改数据,并且我希望在不跟踪它们的情况下执行每个撤消操作跟踪的事情。
到目前为止:
我有办法做到这一切,但我在计数上管理我的"我应该撤消吗"逻辑:
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
),但这两者都不是,因为它们在一个范围内是互斥的——如果你愿意的话,这是以嵌套的方式完成的。这就是为什么它是一个计数,而不是布尔值。
我首先关心的是Program._refCount
可以从多个线程访问,并且它没有被同步。但是您可能会争辩说您的应用程序是单线程的。
下一个更大的问题是,你并没有按照应该使用的方式真正使用一次性模式。我个人认为,以应该使用的方式使用模式很重要,尤其是当你不是唯一一个处理代码的人时。
- 现在,您需要记住必须呼叫
Dispose()
,而不是记住呼叫ResumeUndo()
。问题是:当您的团队成员想要使用HallPass
时,他们是否会自然而然地意识到需要呼叫Dispose ()
?(using
语句很好,但不能在所有场景中都使用。如果有一个HallPass
的寿命超过了单个方法的范围,则不能将其封装在using
语句中) - 尽管忘记在
IDisposible
上调用Dispose()
是不好的,但它通常不会影响程序的正确性——是的,您的程序会出现性能问题、泄漏等,但从功能上讲,它通常仍然是正确的。然而,对于您的HallPass
,如果有人忘记调用Dispose()
,我想会有一个功能错误。而且这个bug很难追踪