【问题标题】:Is there a concept for a "backwards" observer in C#C# 中是否有“向后”观察者的概念
【发布时间】:2021-03-22 20:51:38
【问题描述】:

我正在努力思考一个导致我进入观察者模式的想法,但我不确定它是否是正确的查看位置,而且我在网上找不到任何东西(或者至少,我我没有问正确的问题^^)。

下面的例子:

// 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 方法之一,则侦听器应调用/调用 UpdateUI 方法。

【问题讨论】:

  • 您应该将当前 ToolMode 封装在一个类中(例如,称为 CurrentToolMode),并且其中有一个名为 CurrentMode 的属性,它是当前模式和一个名为 eventModeChanged每当 CurrentToolMode.CurrentMode 更改时都会引发。然后,当CurrentMode 更改时需要做的任何事情都可以订阅该事件。
  • 您正在尝试的任务过于复杂。为什么不能简单地创建私有属性Mode 并在其设置器中只调用一次UpdateUI

标签: c# events observer-pattern


【解决方案1】:

使用完整的属性定义?

// 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的类之外定义,则有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 实现,但出于演示目的没关系)

【讨论】:

    【解决方案2】:

    如果您真的想概括这一点(并且可能这样做,以便您可以响应从响应更改的类外部对 ToolMode 所做的更改),您可以以正常方式使用事件。

    您可以将当前 ToolMode 封装在一个类中,并向该类添加一个在 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 被传入构造函数,以便可以从其他类修改它。或者,您可以在构造函数中创建它,这样其他任何东西都无法更改它 - 这取决于您的使用模式。

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

    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
    

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

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

    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);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-09-29
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 2022-09-25
      • 1970-01-01
      • 1970-01-01
      • 2020-04-08
      相关资源
      最近更新 更多