【问题标题】:C# State pattern and eventsC# 状态模式和事件
【发布时间】:2013-02-24 18:23:50
【问题描述】:

在 C# 中,事件只能由所属类通过受保护的虚拟方法(如 OnClick、OnExit 等)触发……我正在尝试为我的一些复杂类实现 GOF 状态模式。到目前为止一切都很好,除了我找不到从我的状态类中触发拥有类的事件的方法。

例如,我的按钮有两种状态(向上和向下),DownState 类将检查用户输入,并在必要时触发按钮的 Click 事件。

public class Button
{
    public Button()
    {
        State = new ButtonStateNormal(this);
    }

    public event EventHandler<MouseEventArgs> Click;
    public ButtonState State { get; set; }

    protected virtual void OnClick(MouseEventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }
}

public class ButtonStateNormal : ButtonState
{
    Button button;
    public ButtonStateNormal(Button button)
    {
        this.button = button;
    }

    public override void CheckForInput()
    {
        //...
        //Check for user input and change the state
        //...
        button.State = new ButtonStateDown(button);
    }
}

public class ButtonStateDown : ButtonState
{
    Button button;
    public ButtonStateDown(Button button)
    {
        this.button = button;
    }

    public override void CheckForInput()
    {
        //...
        //Check for user input, fire the event of the button and change the state
        //...
        button.OnClick(new MouseEventArgs(mouse.X, mouse.Y)); //I want to do this, obviously it won't work this way
        button.State = new ButtonStateNormal(button);
    }
}

我该怎么做?

【问题讨论】:

标签: c# events design-patterns xna state-pattern


【解决方案1】:

在您的ButtonState 类上设置一个事件或委托,以便您的按钮可以调用。 将Button 中的自动State 属性更改为具有后备存储的属性,该后备存储在状态更改时挂钩和取消挂钩此事件或委托。

public class Button
{
    public Button()
    {
        State = new ButtonStateNormal(this);
    }
    private ButtonState _state;
    public event EventHandler<MouseEventArgs> Click;
    public ButtonState State
    {
        protected get
        {
            return _state;
        }
        set
        {
            if (_state == value)
                return;
            if (_state != null)
                _state.Click -= OnClick;
            if (value != null)
                value.Click += OnClick;

            _state = value;

        }
    }

    protected virtual void OnClick(MouseEventArgs e)
    {
        if (Click != null)
            Click(this, e);
    }

    public void CheckForInput(){
        State.CheckForInput();
    }
}

public abstract class ButtonState
{
    public abstract void CheckForInput();
    public ClickDelegate Click;
    public delegate void ClickDelegate(MouseEventArgs a);
}

然后您可以在具体的 State 类中执行此操作

public class ButtonStateDown : ButtonState
{
    Button button;
    public ButtonStateDown(Button button)
    {
        this.button = button;
    }

    public void CheckForInput()
    {
        //...
        //Check for user input, fire the event of the button and change the state
        //...
        if(Click != null)
            Click(new MouseEventArgs(mouse.X, mouse.Y))
        button.State = new ButtonStateNormal(button);
    }
}

【讨论】:

  • 谢谢。像 ButtonStateNormal 这样不触发事件的状态呢?会不会更好 1. 让每个状态都有所有的事件,并且按钮在状态发生变化时订阅所有事件。或者 2. 在改变状态之前做类型检查,按钮只订阅合适的。
  • 如果您使用选项 2,将事件/委托移动到具体的状态应该相当容易,并为每个事件创建一个接口,您可以使用该接口来检查状态是否实现了该接口看看是否应该连接事件。另一种选择是传入方法句柄,让 State 决定它想要哪些。我现在对那部分没有明确的答案,但会让它在我的脑海中成熟一点。
  • 谢谢。我从来没有想过让状态有自己的按钮订阅的事件。标记为答案并投票赞成。
猜你喜欢
  • 2010-11-26
  • 1970-01-01
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多