2010-07-15 56 views
9

在我当前的项目中,我使用的类实现了以下ITransaction接口,如下所示。这是可以撤消的事务的通用接口。我也有一个TransactionSet类,用于尝试多个事务或事务集,并最终可用于创建事务树。可以从Dispose()中引发事件吗?

ITransaction的某些实现会保留对对象实例或对象的临时引用,以便在以后调用Undo()时使用。稍后可以确认成功的交易,之后不再允许Undo(),并且因此也不再需要临时数据。目前我使用Dispose()作为我的确认方法来清理任何临时资源。

但是,现在我希望我的交易也能够引发事件以通知其他类别发生了什么。除非交易确认,否则我不希望事件触发。因为我不希望允许事务通过撤消多次激发事件,然后再次运行。

由于我使用Dispose()来确认事务是否还存在任何错误,因此也从这些事件中引发了这些事件?或者,在我的界面上单独使用Confirm()方法会更好,除了清除临时数据的Dispose()之外还会触发事件?我不能想到任何我想确认的情况,但不能处理交易。然而,我不清楚我在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; } 
} 

回答

3

IDisposable只是一个运行时集成的设计模式,它以比终结更高效的方式来帮助清理对象。在处置方法中,你“不能”做得很少,但是你应该谨慎地做某些事情。

尽管IDisposable.Dispose()方法不是“真正的”析构函数或终结器,但如果其他对象在处理事件期间维护(或者甚至可能带有)对处置对象的引用,则它可能会对对象的生命周期产生不利影响。如果你对如何实现这样一个系统小心,你可以减轻可能的副作用。然而,重要的是要意识到这种实现提供的潜力,例如,恶意编码器的攻击面越大,例如保持事务对象无限期地活动。

+0

许多.net类都有一个“Disposing”事件,如果没有这个事件将非常困难。如果一个对象将持有对其他地方可能使用或可能不会使用的IDisposable的引用(例如,一个持有BackgroundImage的控件),则可以使用Disposing事件来允许主对象的持有者在必要时清理嵌套对象。 – supercat 2011-03-15 17:52:59

4

处置不是一个特殊的方法 - 它不像一个构造函数或终结或任何东西 - 它只是一个有用的模式来通知消费者在使用它做了一个对象。没有理由不能举办活动。

+0

+1。是的,'Dispose()'不是终结者;做任何你想在那里。 – 2010-07-15 04:38:38

+6

+1。请注意,实现'IDisposable'的“推荐”模式具有'Dispose()'和'Dispose(bool)'方法。当'Dispose(false)'被调用时,方法*被*从终结器调用,并且事件*不能被引发。 – 2010-07-15 11:22:11

0

配置应该简单清理。我将实现Confirm()和Rollback()方法,如果dispose被调用时没有先调用它们,那么应该至少记录一个错误。

0

当然,您可以在Dispose方法中触发任何事件。但是,如果您想要触发事件以确认事务是否存在,我认为您应该有单独的方法来触发事件。 Dispose()是清理内部资源或将内部实例作为众所周知的模式处理的方式。处置后,您的交易安装应该不存在或不再使用。因此,您可以考虑使用单独的方法来确认暂时不可用,并在交易中标记或状态以表明该情况。

1

知道这个问题已经在4年前问过了,但不满足于我的答案,我添加了一个将答案中讨论的一些要点和评论与其他方面相结合的问题。

定稿: 作为@jrista指出的那样,让我们​​清楚了IDisposable无关与GC或完成本身 - 它只是一个惯例,强烈推荐的做法。但是,使用Dispose pattern可以从终结器中调用Dispose方法(如@Stephen Cleary指出的)。在这种情况下,你绝对不应该提出任何事件,nor should it access other managed objects就此事。

抛开Dispose/Finalizer问题,因为你的类不需要一个Finalizer,因为它们不包装非托管资源,不过还是有其他问题。

内存泄漏/ Liftetime匹配: 这是活动的被提及的问题,并可能适用于您的交易实现了。如果您的事件发布者的寿命超过事件订阅者的寿命,那么如果该订阅者没有取消订阅该事件,则可能会发生内存泄漏,因为发布者将继续坚持。如果您的交易相当长时间,并且您订购了许多短期对象,您应该考虑在这些对象中实施处置,然后取消订阅交易。见最少意外Should I always disconnect event handlers in the Dispose method?

原理: 这是个好主意,“滥用”的处置提交事务?我会说不,尽管有先例。以Stream为例。通常Stream.Dispose被实现来调用Flush并且因此将数据提交到底层介质。但是,请注意,我们有一个明确的Flush方法,所以您应该添加该方法。我发现“处置承诺”违反了最不让人意外的原则,明确的方法更加清晰(如果这是您期望的默认行为,您仍然可以从Dispose中调用此方法)。

事件级联/无效对象状态:我认为这是在Dispose没有引发事件的最强争论。事件具有级联的倾向(即一个事件触发其他事件和代码),如果你不小心,最终可能会导致一些代码决定回调正在处理的对象是一个好主意并因此可能处于无效状态。没有乐趣去调试,特别是如果对象可能被多个线程访问!尽管如此,还有像Component.Disposed这样的先例。

我建议不要从Dispose方法引发事件。当你结束事件发布者的生命周期时,它的所有订阅者都相应地更新他们的状态是否真的很重要?在大多数情况下,我发现我无论如何都摆脱了整个对象图(即发布者比订阅者的寿命更长)。在某些情况下,您可能还希望主动抑制在处置过程中发生的任何故障状况(例如关闭TCP连接时)。

相关问题