一种禁用事件处理的设计模式

Rys*_*gan 7 c# wpf design-patterns

为了简单说明我的困境,让我说我有以下代码:

class A
{
    // May be set by a code or by an user.
    public string Property
    {
        set { PropertyChanged(this, EventArgs.Empty); }
    }

    public EventHandler PropertyChanged;
}

class B
{
    private A _a;

    public B(A a)
    {
        _a = a;
        _a.PropertyChanged += Handler;
    }

    void Handler(object s, EventArgs e)
    {
        // Who changed the Property?
    }

    public void MakeProblem()
    {
        _a.Property = "make a problem";
    }
}
Run Code Online (Sandbox Code Playgroud)

为了履行其职责,B级必须对A的PropertyChanged事件作出反应,但在某些情况下也能够自行交替该属性.不幸的是,其他对象也可以与Property进行交互.

我需要一个顺序流的解决方案.也许我可以使用一个变量来禁用一个动作:

bool _dontDoThis;

void Handler(object s, EventArgs e)
{
    if (_dontDoThis)
        return;

    // Do this!
}

public void MakeProblem()
{
    _dontDoThis = true;
    _a.Property = "make a problem";
    _dontDoThis = false;
}
Run Code Online (Sandbox Code Playgroud)

有更好的方法吗?

其他考虑因素

  • 我们无法改变A.
  • A是密封的.
  • PropertyChanged事件还有其他各方联系,我不知道他们是谁.但是当我从B更新属性时,不应该通知它们.但我无法将他们与活动断开,因为我不了解他们.
  • 如果还有更多线程可以同时与该属性进行交互,该怎么办?

解决的子弹越多越好.

原来的问题

我最初的问题是TextBox(WPF),我想根据其内容和焦点进行补充.所以我需要对TextChanged事件作出反应,如果它的来源是从我的补充中派生的,我还需要省略该事件.在某些情况下,不应通知TextChanged事件的其他侦听器.某些状态和样式的某些字符串对其他人不可见.

Rys*_*gan 0

我找到了一种关于与该财产相关的第三方的解决方案,我们不想在该财产发生变化时通知他们。

但有以下要求:

  • 我们有能力推翻A。
  • A 有一个虚拟方法,当属性更改时会调用该方法,并允许暂停要引发的事件。
  • 当属性发生更改时,该事件立即引发。

解决办法是将A替换为MyA,如下:

class A
{
    // May be set by a code or by an user.
    public string Property
    {
        set { OnPropertyChanged(EventArgs.Empty); }
    }

    // This is required
    protected virtual void OnPropertyChanged(EventArgs e)
    {
        PropertyChanged(this, e);
    }

    public EventHandler PropertyChanged;
}

// Inject MyA instead of A
class MyA : A
{
    private bool _dontDoThis;

    public string MyProperty
    {
        set
        {
            try
            {
                _dontDoThis = true;
                Property = value;
            }
            finally
            {
                _dontDoThis = false;
            }
        }
    }

    protected override void OnPropertyChanged(EventArgs e)
    {
        // Also third parties will be not notified
        if (_dontDoThis)
            return;

        base.OnPropertyChanged(e);
    }
}

class B
{
    private MyA _a;

    public B(MyA a)
    {
        _a = a;
        _a.PropertyChanged += Handler;
    }

    void Handler(object s, EventArgs e)
    {
        // Now we know, that the event is not raised by us.
    }

    public void MakeProblem()
    {
        _a.MyProperty = "no problem";
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是我们仍然使用 back bool 字段并且我们假设单个线程。为了摆脱第一个,我们可以使用EZSlaver建议的重构解决方案(此处)。首先,创建一个一次性包装纸:

class Scope
{
    public bool IsLocked { get; set; }

    public static implicit operator bool(Scope scope)
    {
        return scope.IsLocked;
    }
}

class ScopeGuard : IDisposable
{
    private Scope _scope;

    public ScopeGuard(Scope scope)
    {
        _scope = scope;
        _scope.IsLocked = true;
    }

    public void Dispose()
    {
        _scope.IsLocked = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后 MyProperty 可能会重构为:

private readonly Scope _dontDoThisScope = new Scope();

public string MyProperty
{
    set
    {
        using (new ScopeGuard (_dontDoThisScope))
            Property = value;
    }
}
Run Code Online (Sandbox Code Playgroud)