【问题标题】:Super-simple example of C# observer/observable with delegates带有委托的 C# 观察者/可观察的超级简单示例
【发布时间】:2010-11-17 23:35:45
【问题描述】:

我最近开始深入研究 C#,但我一生无法弄清楚在该语言中实现观察者/可观察模式时委托是如何工作的。

谁能给我一个超级简单的例子来说明它是如何完成的?我用谷歌搜索了这个,但我发现的所有示例要么过于具体,要么过于“臃肿”。

【问题讨论】:

    标签: c# events delegates observer-pattern


    【解决方案1】:

    观察者模式通常用events实现。

    这是一个例子:

    using System;
    
    class Observable
    {
        public event EventHandler SomethingHappened;
    
        public void DoSomething() =>
            SomethingHappened?.Invoke(this, EventArgs.Empty);
    }
    
    class Observer
    {
        public void HandleEvent(object sender, EventArgs args)
        {
            Console.WriteLine("Something happened to " + sender);
        }
    }
    
    class Test
    {
        static void Main()
        {
            Observable observable = new Observable();
            Observer observer = new Observer();
            observable.SomethingHappened += observer.HandleEvent;
    
            observable.DoSomething();
        }
    }
    

    有关更多详细信息,请参阅链接文章。

    请注意,上面的示例使用 C# 6 null-conditional 运算符安全地实现 DoSomething 以处理 SomethingHappened 尚未订阅并因此为 null 的情况。如果您使用的是旧版本的 C#,则需要如下代码:

    public void DoSomething()
    {
        var handler = SomethingHappened;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
    

    【讨论】:

    • 为了节省几行代码并避免空检查,请像这样初始化您的事件:stackoverflow.com/questions/340610/…
    • @Dinah:这并不能避免空值检查。您仍然可以稍后设置SomethingHappened = null(如果是一种方便但不理想的取消订阅所有处理程序的方法),因此始终需要进行空检查。
    • @DanPuzey:你可以在课堂上,但同样你可以确保你不要这样做 - 而其他代码不能这样做,因为它只能订阅和取消订阅。如果你确保在你的类中从不故意将其设置为 null,那么可以避免 null 检查。
    • @JonSkeet:当然,我忘记了你不能在课堂外这样做。道歉!
    • 我认为您可以将 DoSomething 中的所有内容替换为 SomethingHappened?.Invoke(this, EventArgs.Empty);
    【解决方案2】:

    在此模型中,您的发布者会执行一些逻辑并发布“事件”。
    然后,发布者将仅将其事件发送给已订阅接收特定事件的订阅者。

    在 C# 中,任何对象都可以发布一组其他应用程序可以订阅的事件。
    当发布类引发事件时,所有订阅的应用程序都会收到通知。
    下图展示了这种机制。

    关于 C# 中事件和委托的最简单示例:

    代码是不言自明的,我还添加了 cmets 以清除代码。

      using System;
    
    public class Publisher //main publisher class which will invoke methods of all subscriber classes
    {
        public delegate void TickHandler(Publisher m, EventArgs e); //declaring a delegate
        public TickHandler Tick;     //creating an object of delegate
        public EventArgs e = null;   //set 2nd paramter empty
        public void Start()     //starting point of thread
        {
            while (true)
            {
                System.Threading.Thread.Sleep(300);
                if (Tick != null)   //check if delegate object points to any listener classes method
                {
                    Tick(this, e);  //if it points i.e. not null then invoke that method!
                }
            }
        }
    }
    
    public class Subscriber1                //1st subscriber class
    {
        public void Subscribe(Publisher m)  //get the object of pubisher class
        {
            m.Tick += HeardIt;              //attach listener class method to publisher class delegate object
        }
        private void HeardIt(Publisher m, EventArgs e)   //subscriber class method
        {
            System.Console.WriteLine("Heard It by Listener");
        }
    
    }
    public class Subscriber2                   //2nd subscriber class
    {
        public void Subscribe2(Publisher m)    //get the object of pubisher class
        {
            m.Tick += HeardIt;               //attach listener class method to publisher class delegate object
        }
        private void HeardIt(Publisher m, EventArgs e)   //subscriber class method
        {
            System.Console.WriteLine("Heard It by Listener2");
        }
    
    }
    
    class Test
    {
        static void Main()
        {
            Publisher m = new Publisher();      //create an object of publisher class which will later be passed on subscriber classes
            Subscriber1 l = new Subscriber1();  //create object of 1st subscriber class
            Subscriber2 l2 = new Subscriber2(); //create object of 2nd subscriber class
            l.Subscribe(m);     //we pass object of publisher class to access delegate of publisher class
            l2.Subscribe2(m);   //we pass object of publisher class to access delegate of publisher class
    
            m.Start();          //starting point of publisher class
        }
    }
    

    输出:

    被听众听到

    Listener2 听到的

    被听众听到

    Listener2 听到的

    听者听过 . . . (无限次)

    【讨论】:

      【解决方案3】:

      这是一个简单的例子:

      public class ObservableClass
      {
          private Int32 _Value;
      
          public Int32 Value
          {
              get { return _Value; }
              set
              {
                  if (_Value != value)
                  {
                      _Value = value;
                      OnValueChanged();
                  }
              }
          }
      
          public event EventHandler ValueChanged;
      
          protected void OnValueChanged()
          {
              if (ValueChanged != null)
                  ValueChanged(this, EventArgs.Empty);
          }
      }
      
      public class ObserverClass
      {
          public ObserverClass(ObservableClass observable)
          {
              observable.ValueChanged += TheValueChanged;
          }
      
          private void TheValueChanged(Object sender, EventArgs e)
          {
              Console.Out.WriteLine("Value changed to " +
                  ((ObservableClass)sender).Value);
          }
      }
      
      public class Program
      {
          public static void Main()
          {
              ObservableClass observable = new ObservableClass();
              ObserverClass observer = new ObserverClass(observable);
              observable.Value = 10;
          }
      }
      

      注意:

      • 这违反了一条规则,即我不会从 observable 中解开观察者,这对于这个简单的示例来说可能已经足够了,但请确保不要让观察者像那样挂在你的事件上。处理这种情况的一种方法是使 ObserverClass IDisposable,并让 .Dispose 方法执行与构造函数中的代码相反的操作
      • 不执行错误检查,至少应在 ObserverClass 的构造函数中进行空检查

      【讨论】:

        【解决方案4】:

        我将上面的几个很好的例子(一如既往地感谢Mr. SkeetMr. Karlsen)捆绑在一起,以包含几个不同的 Observable,并利用一个接口在 Observer 中跟踪它们并允许Observer 通过内部列表“观察”任意数量的 Observable:

        namespace ObservablePattern
        {
            using System;
            using System.Collections.Generic;
        
            internal static class Program
            {
                private static void Main()
                {
                    var observable = new Observable();
                    var anotherObservable = new AnotherObservable();
        
                    using (IObserver observer = new Observer(observable))
                    {
                        observable.DoSomething();
                        observer.Add(anotherObservable);
                        anotherObservable.DoSomething();
                    }
        
                    Console.ReadLine();
                }
            }
        
            internal interface IObservable
            {
                event EventHandler SomethingHappened;
            }
        
            internal sealed class Observable : IObservable
            {
                public event EventHandler SomethingHappened;
        
                public void DoSomething()
                {
                    var handler = this.SomethingHappened;
        
                    Console.WriteLine("About to do something.");
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                }
            }
        
            internal sealed class AnotherObservable : IObservable
            {
                public event EventHandler SomethingHappened;
        
                public void DoSomething()
                {
                    var handler = this.SomethingHappened;
        
                    Console.WriteLine("About to do something different.");
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                }
            }
        
            internal interface IObserver : IDisposable
            {
                void Add(IObservable observable);
        
                void Remove(IObservable observable);
            }
        
            internal sealed class Observer : IObserver
            {
                private readonly Lazy<IList<IObservable>> observables =
                    new Lazy<IList<IObservable>>(() => new List<IObservable>());
        
                public Observer()
                {
                }
        
                public Observer(IObservable observable) : this()
                {
                    this.Add(observable);
                }
        
                public void Add(IObservable observable)
                {
                    if (observable == null)
                    {
                        return;
                    }
        
                    lock (this.observables)
                    {
                        this.observables.Value.Add(observable);
                        observable.SomethingHappened += HandleEvent;
                    }
                }
        
                public void Remove(IObservable observable)
                {
                    if (observable == null)
                    {
                        return;
                    }
        
                    lock (this.observables)
                    {
                        observable.SomethingHappened -= HandleEvent;
                        this.observables.Value.Remove(observable);
                    }
                }
        
                public void Dispose()
                {
                    for (var i = this.observables.Value.Count - 1; i >= 0; i--)
                    {
                        this.Remove(this.observables.Value[i]);
                    }
                }
        
                private static void HandleEvent(object sender, EventArgs args)
                {
                    Console.WriteLine("Something happened to " + sender);
                }
            }
        }
        

        【讨论】:

        • 我知道这是旧的,但是......这看起来线程安全,但它不是。在 Observer.Add 和 Observer.Remove 中,空检查需要在锁内。 Dispose 还应该获取锁,并设置一个 isDispised 标志。否则是一个很好的完整示例。
        【解决方案5】:

        根据MSDN,将观察者模式c#中的委托和事件一起命名为“事件模式”这是一个细微的变化。

        在本文中,您将找到结构良好的示例,说明如何在 c# 中以经典方式以及使用委托和事件来应用该模式。

        Exploring the Observer Design Pattern

        public class Stock
        {
        
            //declare a delegate for the event
            public delegate void AskPriceChangedHandler(object sender,
                  AskPriceChangedEventArgs e);
            //declare the event using the delegate
            public event AskPriceChangedHandler AskPriceChanged;
        
            //instance variable for ask price
            object _askPrice;
        
            //property for ask price
            public object AskPrice
            {
        
                set
                {
                    //set the instance variable
                    _askPrice = value;
        
                    //fire the event
                    OnAskPriceChanged();
                }
        
            }//AskPrice property
        
            //method to fire event delegate with proper name
            protected void OnAskPriceChanged()
            {
        
                AskPriceChanged(this, new AskPriceChangedEventArgs(_askPrice));
        
            }//AskPriceChanged
        
        }//Stock class
        
        //specialized event class for the askpricechanged event
        public class AskPriceChangedEventArgs : EventArgs
        {
        
            //instance variable to store the ask price
            private object _askPrice;
        
            //constructor that sets askprice
            public AskPriceChangedEventArgs(object askPrice) { _askPrice = askPrice; }
        
            //public property for the ask price
            public object AskPrice { get { return _askPrice; } }
        
        }//AskPriceChangedEventArgs
        

        【讨论】:

          【解决方案6】:
              /**********************Simple Example ***********************/    
          
          class Program
                  {
                      static void Main(string[] args)
                      {
                          Parent p = new Parent();
                      }
                  }
          
                  ////////////////////////////////////////////
          
                  public delegate void DelegateName(string data);
          
                  class Child
                  {
                      public event DelegateName delegateName;
          
                      public void call()
                      {
                          delegateName("Narottam");
                      }
                  }
          
                  ///////////////////////////////////////////
          
                  class Parent
                  {
                      public Parent()
                      {
                          Child c = new Child();
                          c.delegateName += new DelegateName(print);
                          //or like this
                          //c.delegateName += print;
                          c.call();
                      }
          
                      public void print(string name)
                      {
                          Console.WriteLine("yes we got the name : " + name);
                      }
                  }
          

          【讨论】:

            【解决方案7】:

            我不想更改我的源代码来添加额外的观察者,所以我写了以下简单的例子:

            //EVENT DRIVEN OBSERVER PATTERN
            public class Publisher
            {
                public Publisher()
                {
                    var observable = new Observable();
                    observable.PublishData("Hello World!");
                }
            }
            
            //Server will send data to this class's PublishData method
            public class Observable
            {
                public event Receive OnReceive;
            
                public void PublishData(string data)
                {
                    //Add all the observer below
                    //1st observer
                    IObserver iObserver = new Observer1();
                    this.OnReceive += iObserver.ReceiveData;
                    //2nd observer
                    IObserver iObserver2 = new Observer2();
                    this.OnReceive += iObserver2.ReceiveData;
            
                    //publish data 
                    var handler = OnReceive;
                    if (handler != null)
                    {
                        handler(data);
                    }
                }
            }
            
            public interface IObserver
            {
                void ReceiveData(string data);
            }
            
            //Observer example
            public class Observer1 : IObserver
            {
                public void ReceiveData(string data)
                {
                    //sample observers does nothing with data :)
                }
            }
            
            public class Observer2 : IObserver
            {
                public void ReceiveData(string data)
                {
                    //sample observers does nothing with data :)
                }
            }
            

            【讨论】:

              【解决方案8】:

              类似这样的:

              // interface implementation publisher
              public delegate void eiSubjectEventHandler(eiSubject subject);
              
              public interface eiSubject
              {
                  event eiSubjectEventHandler OnUpdate;
              
                  void GenereteEventUpdate();
              
              }
              
              // class implementation publisher
              class ecSubject : eiSubject
              {
                  private event eiSubjectEventHandler _OnUpdate = null;
                  public event eiSubjectEventHandler OnUpdate
                  {
                      add
                      {
                          lock (this)
                          {
                              _OnUpdate -= value;
                              _OnUpdate += value;
                          }
                      }
                      remove { lock (this) { _OnUpdate -= value; } }
                  }
              
                  public void GenereteEventUpdate()
                  {
                      eiSubjectEventHandler handler = _OnUpdate;
              
                      if (handler != null)
                      {
                          handler(this);
                      }
                  }
              
              }
              
              // interface implementation subscriber
              public interface eiObserver
              {
                  void DoOnUpdate(eiSubject subject);
              
              }
              
              // class implementation subscriber
              class ecObserver : eiObserver
              {
                  public virtual void DoOnUpdate(eiSubject subject)
                  {
                  }
              }
              

              observer pattern C# with event . link to the repository

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-08-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-06-09
                • 2021-04-13
                • 1970-01-01
                相关资源
                最近更新 更多