C# 中是否有 "backwards" 观察者的概念

Is there a concept for a "backwards" observer in C#

我正在努力思考一个让我想到观察者模式的想法,但我不确定它是不是合适的地方,而且我在网上找不到任何东西(或者至少, 我问的问题不对^^).

以下示例:

// just an enum for a different tool in the ui
private ToolMode _mode;

// updates the UI according to the selected mode
private void UpdateUI(){...};

private void ChangeUIToolToMode1()
{ 
  _mode = ToolMode.Mode1;
  UpdateUI();
}

private void ChangeUIToolToMode2()
{ 
  _mode = ToolMode.Mode2;
  UpdateUI();
}

在这个例子中,我的问题是我必须在每个 ChangeUITool 方法中实现 UpadteUI() 调用。我正在寻找一种方法,将它们添加到一种调用 UpdateUI 方法的侦听器中,而不管调用哪种方法,因为应用程序可能会增长。

类似这样的伪代码:

private ToolMode _mode;

private Listener _listener;

private Init()
{
  _listener= new Listener(UpdateUI);
  _listener.invokingEvents.Add(ChangeUIToolToMode1);
  _listener.invokingEvents.Add(ChangeUIToolToMode2);
}

如果现在调用 ChangeUITool 方法之一,侦听器应该 call/invoke UpdateUI 方法。

使用完整的 属性 定义?

// property can be private or protected as well, depending on your requirements
public ToolMode Mode
{
    get { return _mode; }
    set
    {
        if (_mode != value)
        {
            _mode = value;
            UpdateUI();
        }
    }
}

如果UpdateUI必须在包含_mode的class之外定义,则有INotyfyPropertyChanged接口和事件:

class Foo : INotifyPropertyChanged
{
    private ToolMode _mode;

    public ToolMode Mode
    {
        get { return _mode; }
        set
        {
            if (_mode != value)
            {
                _mode = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Mode)));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

class Bar
{
    private readonly Foo _foo;

    public Bar()
    {
        _foo = new Foo();
        _foo.PropertyChanged += (sender, args) => UpdateUI();
    }

    private void UpdateUI()
    {
    }

    // ...
}

(这不是很好的 INPC 实现,但为了演示目的它没问题)

如果您真的想对此进行概括(并且可能这样做以便您可以响应从正在响应更改的 class 外部对 ToolMode 所做的更改),您可以在正常情况下使用事件方式。

您可以将当前的 ToolMode 封装在 class 中,然后向其中添加一个 class 事件,该事件将在 ToolMode 更改时引发。例如:

public sealed class CurrentToolMode
{
    public ToolMode CurrentMode { get; private set; }

    public event EventHandler ModeChanged;

    public void ChangeMode(ToolMode newMode)
    {
        if (newMode == CurrentMode)
            return;

        CurrentMode = newMode;
        ModeChanged?.Invoke(this, EventArgs.Empty);
    }
}

然后像这样使用它:

public sealed class Test
{
    public Test(CurrentToolMode mode)
    {
        _mode = mode;
        _mode.ModeChanged += onModeChanged;
    }

    void onModeChanged(object sender, EventArgs e)
    {
        UpdateUI();
    }

    void UpdateUI()
    {
        Console.WriteLine("UpdateUI() called; mode was changed to " + _mode.CurrentMode);
    }

    public void SetMode1()
    {
        _mode.ChangeMode(ToolMode.Mode1);
    }

    public void SetMode2()
    {
        _mode.ChangeMode(ToolMode.Mode2);
    }

    readonly CurrentToolMode _mode;
}

在上面的示例中,CurrentToolMode 被传递给构造函数,因此可以从其他 classes 修改它。或者,您可以在构造函数中创建它,这样其他任何东西都无法更改它 - 这取决于您的使用模式。

这是一个可编译的控制台应用程序,演示了它的用法:

using System;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            Test test = new Test(new CurrentToolMode());
            test.SetMode1();
            test.SetMode2();
        }
    }

    public sealed class Test
    {
        public Test(CurrentToolMode mode)
        {
            _mode = mode;
            _mode.ModeChanged += onModeChanged;
        }

        void onModeChanged(object sender, EventArgs e)
        {
            UpdateUI();
        }

        void UpdateUI()
        {
            Console.WriteLine("UpdateUI() called; mode was changed to " + _mode.CurrentMode);
        }

        public void SetMode1()
        {
            _mode.ChangeMode(ToolMode.Mode1);
        }

        public void SetMode2()
        {
            _mode.ChangeMode(ToolMode.Mode2);
        }

        readonly CurrentToolMode _mode;
    }

    public enum ToolMode
    {
        None,
        Mode1,
        Mode2
    }

    public sealed class CurrentToolMode
    {
        public ToolMode CurrentMode { get; private set; }

        public event EventHandler ModeChanged;

        public void ChangeMode(ToolMode newMode)
        {
            if (newMode == CurrentMode)
                return;

            CurrentMode = newMode;
            ModeChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

该代码的输出是:

UpdateUI() called; mode was changed to Mode1
UpdateUI() called; mode was changed to Mode2

我应该强调,如果您只打算在一个 class 中的有限数量的方法中使用这种解决方案,那么这种解决方案完全是矫枉过正,但它展示了一种响应设置更改的通用方法解耦的方式。这种方法允许任意数量的 classes 响应设置更改。

这是一个可编译的示例,显示从 class 外部更改模式以响应模式更改:

using System;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            CurrentToolMode mode = new CurrentToolMode();
            Test test = new Test(mode);
            mode.ChangeMode(ToolMode.Mode1);
            mode.ChangeMode(ToolMode.Mode2);
        }
    }

    public sealed class Test
    {
        public Test(CurrentToolMode mode)
        {
            _mode = mode;
            _mode.ModeChanged += onModeChanged;
        }

        void onModeChanged(object sender, EventArgs e)
        {
            UpdateUI();
        }

        void UpdateUI()
        {
            Console.WriteLine("UpdateUI() called; mode was changed to " + _mode.CurrentMode);
        }

        readonly CurrentToolMode _mode;
    }

    public enum ToolMode
    {
        None,
        Mode1,
        Mode2
    }

    public sealed class CurrentToolMode
    {
        public ToolMode CurrentMode { get; private set; }

        public event EventHandler ModeChanged;

        public void ChangeMode(ToolMode newMode)
        {
            if (newMode == CurrentMode)
                return;

            CurrentMode = newMode;
            ModeChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}