C# 我应该为纯托管资源使用IDisposable吗?

C# 我应该为纯托管资源使用IDisposable吗?,c#,.net,dispose,idisposable,C#,.net,Dispose,Idisposable,以下是场景: 我有一个名为事务的对象,它需要确保在任何给定时间只有一个实体有权编辑它 为了方便使用长寿命锁,我让类生成一个令牌对象,该对象可用于进行编辑 您可以这样使用它: var transaction = new Transaction(); using (var tlock = transaction.Lock()) { transaction.Update(data, tlock); } public sealed class Transaction { privat

以下是场景:

我有一个名为
事务
的对象,它需要确保在任何给定时间只有一个实体有权编辑它

为了方便使用长寿命锁,我让类生成一个令牌对象,该对象可用于进行编辑

您可以这样使用它:

var transaction = new Transaction();

using (var tlock = transaction.Lock())
{
    transaction.Update(data, tlock);
}
public sealed class Transaction
{
    private readonly object lockMutex = new object();

    private TransactionLock currentLock;

    public TransactionLock Lock()
    {
        lock (this.lockMutex)
        {
            if (this.currentLock != null)
                throw new InvalidOperationException(/* ... */);

            this.currentLock = new TransactionLock(this);
            return this.currentLock;
        }
    }

    public void Update(object data, TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            // ...
        }
    }

    internal void ValidateLock(TransactionLock tlock)
    {
        if (this.currentLock == null)
            throw new InvalidOperationException(/* ... */);

        if (this.currentLock != tlock)
            throw new InvalidOperationException(/* ... */);
    }

    internal void Unlock(TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            this.currentLock = null;
        }
    }
}
现在,我想让
TransactionLock
类实现
IDisposable
,这样它的用法就可以很清楚了。但是,我没有任何非托管资源可供处置。然而,TransctionLock对象本身是一种“非托管资源”,因为CLR不知道如何正确地完成它

所有这些都很好,我只需要使用
IDisposable
就可以了

但是,当我尝试在终结器中执行此操作时,我的问题出现了:

~TransactionLock()
{
    this.Dispose(false);
}
如果可能的话,我希望终结器从锁中释放事务如何在终结器中检测父事务(
this.transaction
)是否已终结?

我是否应该使用更好的模式?

而且,
事务
类本身不必是一次性的,因为它不维护对锁的引用,也不关心它在进入坟墓时是否被解锁


Transaction类如下所示:

var transaction = new Transaction();

using (var tlock = transaction.Lock())
{
    transaction.Update(data, tlock);
}
public sealed class Transaction
{
    private readonly object lockMutex = new object();

    private TransactionLock currentLock;

    public TransactionLock Lock()
    {
        lock (this.lockMutex)
        {
            if (this.currentLock != null)
                throw new InvalidOperationException(/* ... */);

            this.currentLock = new TransactionLock(this);
            return this.currentLock;
        }
    }

    public void Update(object data, TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            // ...
        }
    }

    internal void ValidateLock(TransactionLock tlock)
    {
        if (this.currentLock == null)
            throw new InvalidOperationException(/* ... */);

        if (this.currentLock != tlock)
            throw new InvalidOperationException(/* ... */);
    }

    internal void Unlock(TransactionLock tlock)
    {
        lock (this.lockMutex)
        {
            this.ValidateLock(tlock);

            this.currentLock = null;
        }
    }
}
以及
TransactionLock的
Dispose(bool)
代码:

private void Dispose(bool disposing)
{
    if (disposing)
    {
        if (this.Transaction != null)
        {
            this.Transaction.Unlock(this);
            this.Transaction = null;
        }
    }
}
在终结器中,如何检测 父事务 (此交易)已被删除 最终确定

这可以通过在事务中保留一个_-disposed布尔字段并通过IsDisposed只读属性公开它来实现。这是标准做法

   ~TransactionLock()
    {
        this.Dispose(false);
    }
有更好的模式吗 使用

如果TransactionLock没有非托管资源是正确的,那么只需省略析构函数(终结器)。它没有任何功能,但它确实有相当大的成本

编辑:如果我读得正确,Unlock不会切断TransactionLock到TTransaction的链接,这意味着将通过析构函数调用旧的locks Dispose(bool)。目前还不清楚这是否安全

使用
TransactionLock.Dispose(bool)


此外,事务类本身 不必是一次性的,因为它 不维护对 锁,不在乎是否 当它进入下一步时,它将被解锁 坟墓

由此可知,当收集TransactionLock时,它只能持有对也正在收集的事务的引用。这里不需要干扰析构函数,这不会解决任何问题,只会产生您不需要的问题。

这是一个问题。不过,您的案例要简单得多,您还实现了终结器。这根本是错误的,您在客户端代码中隐藏了一个bug。请注意,终结器在单独的线程上运行。调试一致性死锁比处理随机异步消失的锁容易得多


建议:遵循.NETFramework的指导:不要提供太多帮助。Microsoft出于同样的原因放弃了同步方法。

这与前面提到的不太一样。在我的例子中,正在处理的令牌对象实际上用作验证机制,需要正确地清理。在我的场景中,需要进行的步骤与本机句柄完全相同。好的,那么,有没有办法使用类似于
的语义来获取
,而不实际实现
IDisposable
?我很确定答案是否定的。在这种情况下,有没有办法确定地触发终结器呢?另外,我刚刚发布了我的Dispose代码,正如您所看到的,它并没有释放锁(因为我无法证明事务是否已完成)。这意味着这将导致您提到的一致死锁(或者实际上是,
invalidooperationexception
)。这实际上意味着我的终结器总是一种浪费,因为它从来不会进行任何实际处理。我希望它处理锁,但我想,正如您所建议的,这是“帮助太多了”…我想,那么我真正想做的是模拟C#中的RAII,通过强制(或建议)一个dispose()而不是析构函数/终结器。有没有一种方法可以实现当对象超出范围时立即调用的终结器?@John:对于RAII,只需实现Dispose()并使用
using
。仍然不需要析构函数。在什么情况下,
TransactionLock
实际上可以被放弃,而不调用
Dispose
?我看不到终结器能够做任何有用的事情。