我正在实现一个类库,并寻找一种方法来限制库将分配给预设数量的给定类的实例数量。限制必须是机器范围 - 一个简单的static
计数器是不够的,因为这只会计算调用过程中的实例。我想保留尽可能简单的东西(没有内存映射文件等)并且尽可能安全(没有在临时文件或注册表中存储计数器),因此决定尝试使用全局共享信号作为“计数器”全局类实例计数(使用信号量)
public class MyClass : IDisposable
{
// Limit to 10 instances
Semaphore m_sem = new Semaphore(10, 10, "SharedName");
public MyClass()
{
if(!m_sem.WaitOne(0))
{
throw new Exception("No instances free");
}
}
void IDisposable.Dispose()
{
m_sem.Release();
}
}
这似乎工作正常。然而,如果Dispose()
不被调用,信号量永远不会被释放 - 实质上是“泄漏”实例。现在恕我直言IDisposable
是.NET最糟糕的部分之一 - 我看到远远比它更多的代码using(...) {}
。更糟糕的是,当您使用IDisposable
作为数据成员并且在您的应用程序的每个课程中都会看到“IDisposable癌症”传播。
因此,我决定为忘记using()的人实施完整的IDisposable
(反模式)模式。
public class MyClass : IDisposable
{
// Limit to 10 instances
Semaphore m_sem = new Semaphore(10, 10, "SharedName");
public MyClass()
{
if(!m_sem.WaitOne(0))
{
throw new Exception("No instances left");
}
}
~MyClass()
{
Dispose(false);
}
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
void Dispose(bool disposing)
{
if(disposing)
{
m_sem.Release();
}
else
{
// To release or not release?
// m_sem.Release();
}
}
}
一切都简单而具有using
正确调用的时候,我松开semapore。但是,当被最终确定为最后的手段时,据我了解,我不应该访问受管资源,因为销毁顺序不是固定的 - m_sem可能已被销毁。
那么,如何在用户忘记using
的情况下释放信号? (RTFM可能是一个有效的答案,但我希望避免)。就目前而言,“泄漏”的实例直到最终使用我的程序集的过程终止(此时我假设全局信号量被释放)
或者的确有没有更好的方法来做到这一点?
经过多次阅读,即使MS自己的代码似乎在这个问题上不一致。有时,实例在从终结器调用时,从不会在IDisposable数据成员上调用Dispose(),有时他们会这样做。这一切都似乎基于MS自己的知识,包含什么类的内幕正在做的事情 - 围绕非托管的瘦托管包装是安全的,而不是。还有另一个讨厌IDisposable的原因,我猜想:)。 –