C# 是否可以从Dispose()触发事件?
在我当前的项目中,我使用的类实现了如下所示的C# 是否可以从Dispose()触发事件?,c#,.net,transactions,idisposable,C#,.net,Transactions,Idisposable,在我当前的项目中,我使用的类实现了如下所示的ITransaction接口。这是可以撤消的事务的通用接口。我还有一个TransactionSet类,用于尝试多个事务或TransactionSet,最终可用于创建事务树 ITransaction的一些实现保留了对对象实例或文件的临时引用,如果调用了Undo(),这些对象实例或文件以后可能会使用。稍后可以确认成功的事务,此后不再允许Undo(),因此也不再需要临时数据。目前,我正在使用Dispose()作为清除任何临时资源的确认方法 但是,现在我希望我
ITransaction
接口。这是可以撤消的事务的通用接口。我还有一个TransactionSet
类,用于尝试多个事务或TransactionSet,最终可用于创建事务树
ITransaction
的一些实现保留了对对象实例或文件的临时引用,如果调用了Undo()
,这些对象实例或文件以后可能会使用。稍后可以确认成功的事务,此后不再允许Undo()
,因此也不再需要临时数据。目前,我正在使用Dispose()
作为清除任何临时资源的确认方法
但是,现在我希望我的事务也触发事件,以通知其他类发生了什么。我不希望事件触发,除非交易得到确认。因为我不想让一个事务通过撤销多次触发事件,然后再次运行
由于我正在使用Dispose()
确认一个事务,因此也从中触发这些事件是否有任何错误?或者,在我的界面上,除了清除临时数据的Dispose()
方法之外,还有一个单独的Confirm()
方法触发事件,这样会更好吗?我想不出有哪种情况我想确认但不处理交易。然而,我并不完全清楚在Dispose()
中应该做什么,不应该做什么
公共枚举事务状态
{
NotRun,//事务尚未运行,或已撤消回到原始状态
Successful,///操作已运行且已成功
错误//试图运行该操作,但失败
}
///
///通用事务接口
///
公共接口ITransaction
{
TransactionStatus状态{get;}
///
///尝试事务成功时返回true,失败时返回false。
///如果失败,预计一切都将恢复到原始状态。
///如果状态已成功,则不执行任何操作
///
///
bool-Go();
///
///恢复交易记录
///只有状态成功时才会执行某些操作。
///应将状态返回为NotRun
///
无效撤消();
///
///如果状态==错误,则说明错误原因的消息
///否则等于字符串。空
///
字符串错误消息{get;}
}
Dispose不是一种特殊的方法-它不像是一个ctor或终结器或其他任何东西-它只是一种有用的模式,用于通知对象消费者已使用它。没有理由它不能引发事件。Dispose应该简单地进行清理。我将实现Confirm()和Rollback()方法,如果调用dispose时没有先调用它们中的任何一个,那么至少应该记录这是一个错误。IDisposable只是一个运行时集成的设计模式,它以比最终确定更高效的方式促进对象清理。在处置方法中,你“不能”做的事情很少,但是你应该对做一些事情保持警惕
虽然
IDisposable.Dispose()
方法不是“真正的”析构函数或终结器,但如果其他对象在处置事件期间保留(甚至可能获取)对处置对象的引用,则会对对象的生存期产生不利影响。如果您仔细考虑如何实现这样一个系统,您可以减轻可能的副作用。然而,重要的是要认识到这样一种实现所提供的潜力…例如,恶意代码编写者可以通过(比如)无限期地保持事务对象的活动来利用增加的攻击面。当然,您可以在Dispose方法中触发任何事件。然而,如果您想触发事件以确认事务是否存在,我认为您应该有一个单独的方法来触发事件。Dispose()是清理内部资源或以众所周知的模式处置内部实例的方法。处理后,您的事务安装不应该在那里,或者不再使用。因此,您可以考虑一个单独的方法来确认临时将不可用,在事务中用标志或状态来指示, 知道这个问题在4年前被问及,但我不满足于这些答案,我添加了一个将答案和评论中讨论的一些要点与其他方面相结合的答案
定稿:
正如@jrista所指出的,让我们明确一点,IDisposable本身与GC或最终确定无关——它只是一种约定和强烈建议的实践。但是,您可以使用Finalizer调用Dispose方法(如@Stephen Cleary指出的)。在这种情况下,你绝对不应该提出任何事件
抛开Dispose/Finalizer问题不谈,因为您的类不需要终结器,因为它们不包装非托管资源,所以还有其他问题
内存泄漏/Liftetime匹配:
这是事件中经常提到的问题,可能也适用于事务实现。当您的事件发布服务器的生存期超过事件订阅服务器的生存期时,如果该订阅服务器不取消订阅该事件,则可能会发生内存泄漏,因为发布服务器会继续保留该事件。如果您的事务相当长,并且您向它们订阅了许多短期对象,那么您应该考虑在这些对象中实现dispose,然后取消订阅事务。看
最小意外原则:
提交事务时“滥用”处理是一个好主意吗?虽然有先例,但我会说不。以Stream
为例。通常Stream.Dispose
被实现为调用public enum TransactionStatus
{
NotRun, // the Transaction has not been run, or has been undoed back to the original state
Successful, ///the action has been run and was successful
Error //there was an attempt to run the action but it failed
}
/// <summary>
/// Generic transaction interface
/// </summary>
public interface ITransaction
{
TransactionStatus Status { get; }
/// <summary>
/// Attempts the transaction returns true if successful, false if failed.
/// If failed it is expected that everything will be returned to the original state.
/// Does nothing if status is already Successful
/// </summary>
/// <returns></returns>
bool Go();
/// <summary>
/// Reverts the transaction
/// Only does something if status is successful.
/// Should return status to NotRun
/// </summary>
void Undo();
/// <summary>
/// A message describing the cause of the error if Status == Error
/// Otherwise equal String.Empty
/// </summary>
string ErrorMessage { get; }
}