IObserver<T>(IObservable<T>) vs ObservableCollection vs INotifyPropertyChanged

下面是稍微修改过的MSDN的IObservable例子。

    public struct Message
    {
        string text;

        public Message(string newText)
        {
            this.text = newText;
        }

        public string Text
        {
            get
            {
                return this.text;
            }
        }
    }

    public class Headquarters : IObservable<Message>
    {
        public Headquarters()
        {
            observers = new List<IObserver<Message>>();
        }

        private List<IObserver<Message>> observers;

        public IDisposable Subscribe(IObserver<Message> observer)
        {
            if (!observers.Contains(observer))
                observers.Add(observer);
            return new Unsubscriber(observers, observer);
        }

        private class Unsubscriber : IDisposable
        {
            private List<IObserver<Message>> _observers;
            private IObserver<Message> _observer;

            public Unsubscriber(List<IObserver<Message>> observers, IObserver<Message> observer)
            {
                this._observers = observers;
                this._observer = observer;
            }

            public void Dispose()
            {
                if (_observer != null && _observers.Contains(_observer))
                    _observers.Remove(_observer);
            }
        }

        public void SendMessage(Nullable<Message> loc)
        {
            foreach (var observer in observers)
            {
                if (!loc.HasValue)
                    observer.OnError(new MessageUnknownException());
                else
                    observer.OnNext(loc.Value);
            }
        }

        public void EndTransmission()
        {
            foreach (var observer in observers.ToArray())
                if (observers.Contains(observer))
                    observer.OnCompleted();

            observers.Clear();
        }
    }

    public class MessageUnknownException : Exception
    {
        internal MessageUnknownException()
        {
        }
    }

    public class Inspector : IObserver<Message>
    {
        private IDisposable unsubscriber;
        private string instName;

        public Inspector(string name)
        {
            this.instName = name;
        }

        public string Name
        {
            get
            {
                return this.instName;
            }
        }

        public virtual void Subscribe(IObservable<Message> provider)
        {
            if (provider != null)
                unsubscriber = provider.Subscribe(this);
        }

        public virtual void OnCompleted()
        {
            Console.WriteLine("The headquarters has completed transmitting data to {0}.", this.Name);
            this.Unsubscribe();
        }

        public virtual void OnError(Exception e)
        {
            Console.WriteLine("{0}: Cannot get message from headquarters.", this.Name);
        }

        public virtual void OnNext(Message value)
        {
            Console.WriteLine("{1}: Message I got from headquarters: {0}", value.Text, this.Name);
        }

        public virtual void Unsubscribe()
        {
            unsubscriber.Dispose();
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Inspector inspector1 = new Inspector("Greg Lestrade");
            Inspector inspector2 = new Inspector("Sherlock Holmes");

            Headquarters headquarters = new Headquarters();

            inspector1.Subscribe(headquarters);
            inspector2.Subscribe(headquarters);

            headquarters.SendMessage(new Message("Catch Moriarty!"));
            headquarters.EndTransmission();

            Console.ReadKey();
        }
    }

下面是一个用ObservableCollection实现自动保存功能的Collection.

    /// <summary>
    /// Keep objects in the collection mirrored on a disk storage device, and support restore of collection from save disk file
    /// </summary>
    /// <typeparam name="T">Type of object in the collection</typeparam>
    public class AutoSaveCollection<T> : ObservableCollection<T>, IDisposable
    {
        private string fileName;

        private IFormatter formatter = new BinaryFormatter();

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="fileName">Full filename to back the collection on disk</param>
        /// <param name="restoreFromFile">Restore collection from file</param>
        public AutoSaveCollection(string fileName, bool restoreFromFile)
            : base()
        {
            this.fileName = fileName;
            if (restoreFromFile)
            {
                if (File.Exists(this.fileName))
                {
                    Restore();
                }
            }
            else
            {
                if (File.Exists(this.fileName))
                {
                    File.Delete(this.fileName);
                }
            }

            base.CollectionChanged += observableCollection_CollectionChanged;
        }

        /// <summary>
        /// This event handler is triggered whenever the observable collection has been changed.  Trigger save to disk based on the change
        /// </summary>
        /// <param name="sender">self</param>
        /// <param name="e">change type</param>
        private void observableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    if (e.NewItems != null)
                    {
                        SaveNew(e.NewItems.ToArray<T>());
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Reset:
                    SaveAll();
                    break;
            }
        }

        /// <summary>
        /// Discard the disk file.
        /// </summary>
        public void Discard()
        {
            base.Clear();
            if (File.Exists(this.fileName))
            {
                File.Delete(this.fileName);
            }
        }

        /// <summary>
        /// Save everything stored in the collection to disk again (eg when collection item has been removed or when exiting)
        /// </summary>
        public void SaveAll()
        {
            string backupFileName = null;
            try
            {
                backupFileName = Helpers.RenameIfExists(this.fileName);
                using (FileStream stream = new FileStream(this.fileName, FileMode.CreateNew, FileAccess.Write, FileShare.Read))
                {
                    foreach (T item in this)
                    {
                        this.formatter.Serialize(stream, item);
                    }
                }

                try
                {
                    if (File.Exists(backupFileName))
                    {
                        File.Delete(backupFileName);
                    }
                }
                catch (IOException ex)
                {
                    Logger.LogException(ex, "I/O failure");
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex, "Failed to save objects", this.fileName);

                if (!string.IsNullOrEmpty(backupFileName) && File.Exists(backupFileName))
                {
                    File.Move(backupFileName, fileName);
                }

                throw;
            }
        }

        /// <summary>
        /// Save new items directly to disk (don't bother add them to collection (to save memory))
        /// </summary>
        /// <param name="newItems"></param>
        public void SaveNew(params T[] newItems)
        {
            SaveNew((IEnumerable<T>)newItems);
        }

        private void SaveNew(IEnumerable<T> newItems)
        {
            try
            {
                using (FileStream stream = new FileStream(this.fileName, FileMode.Append, FileAccess.Write, FileShare.Read))
                {
                    foreach (T item in newItems)
                    {
                        this.formatter.Serialize(stream, item);
                    }
                }
                return;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex, "Exception adding data to file", this.fileName);
            }

            SaveAll();
        }

        /// <summary>
        /// Restore saved collection from disk file
        /// </summary>
        public void Restore()
        {
            Stream stream = null;
            try
            {
                if (!File.Exists(this.fileName))
                {
                    return;
                }

                base.Clear();

                using (stream = new FileStream(this.fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    while (stream.Position < stream.Length)
                    {
                        T item = (T)this.formatter.Deserialize(stream);
                        if (item != null)
                        {
                            base.Add(item);
                        }
                    }
                }
            }
            catch (IOException ex)
            {
                Logger.LogException(ex, "Exception when restoring checkpoint", fileName);
            }
        }

        /// <summary>
        /// On dispose, save everything in the collection to disk at once.
        /// </summary>
        void IDisposable.Dispose()
        {
            base.CollectionChanged -= observableCollection_CollectionChanged;
            if (base.Count > 0)
            {
                SaveAll();
            }
        }
    }

INotifyPropertyChanged只是一个接口,主要用于当类的属性值发生改变的时候通知,貌似在WPF用的比较多。

public class UserNPC:INotifyPropertyChanged
{
    private string name;
    public string Name { 
        get { return name; } 
        set { name = value; onPropertyChanged(this, "Name"); } 
    }
    public int grade;
    public int Grade { 
        get { return grade; } 
        set { grade = value; onPropertyChanged(this, "Grade"); } 
    }

    // Declare the PropertyChanged event
    public event PropertyChangedEventHandler PropertyChanged;

    // OnPropertyChanged will raise the PropertyChanged event passing the
    // source property that is being updated.
    private void onPropertyChanged(object sender, string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
        }
    }
}

IObserver<T>&IObservable<T>是对设计模式的实现跟总结,ObservableCollection跟INotifyPropertyChanged通过事件来通知。

posted @ 2015-03-10 13:17  bwangff  阅读(577)  评论(0编辑  收藏  举报