public ListenerList GetListenerList(string propertyName) { ListenerList list; if (!String.IsNullOrEmpty(propertyName)) { // source has changed a particular property. Notify targets // who are listening either for this property or for all properties. PropertyRecord pr = (PropertyRecord)_dict[propertyName]; ListenerList <PropertyChangedEventArgs> listeners = (pr == null) ? null : pr.List; PropertyRecord genericRecord = (PropertyRecord)_dict[String.Empty]; ListenerList <PropertyChangedEventArgs> genericListeners = (genericRecord == null) ? null : genericRecord.List; if (genericListeners == null) { if (listeners != null) { list = listeners; // only specific listeners } else { list = ListenerList.Empty; // no listeners at all } } else { if (listeners != null) { // there are both specific and generic listeners - // combine the two lists. list = new ListenerList <PropertyChangedEventArgs>(listeners.Count + genericListeners.Count); for (int i = 0, n = listeners.Count; i < n; ++i) { list.Add(listeners[i]); } for (int i = 0, n = genericListeners.Count; i < n; ++i) { list.Add(genericListeners[i]); } } else { list = genericListeners; // only generic listeners } } } else { // source has changed all properties. Notify all targets. // Use previously calculated combined list, if available. PropertyRecord pr = (PropertyRecord)_dict[AllListenersKey]; ListenerList <PropertyChangedEventArgs> pcList = (pr == null) ? null : pr.List; if (pcList == null) { // make one pass to compute the size of the combined list. // This avoids expensive reallocations. int size = 0; foreach (DictionaryEntry de in _dict) { Debug.Assert((String)de.Key != AllListenersKey, "special key should not appear"); size += ((PropertyRecord)de.Value).List.Count; } // create the combined list pcList = new ListenerList <PropertyChangedEventArgs>(size); // fill in the combined list foreach (DictionaryEntry de in _dict) { ListenerList listeners = ((PropertyRecord)de.Value).List; for (int i = 0, n = listeners.Count; i < n; ++i) { pcList.Add(listeners.GetListener(i)); } } // save the result for future use (see below) _proposedAllListenersList = pcList; } list = pcList; } return(list); }
// event handler for PropertyChanged event private void OnPropertyChanged(object sender, PropertyChangedEventArgs args) { ListenerList list; string propertyName = args.PropertyName; // get the list of listeners using (ReadLock) { // look up the list of listeners HybridDictionary dict = (HybridDictionary)this[sender]; if (dict == null) { // this can happen when the last listener stops listening, but the // source raises the event on another thread after the dictionary // has been removed (bug 1235351) list = ListenerList.Empty; } else if (!String.IsNullOrEmpty(propertyName)) { // source has changed a particular property. Notify targets // who are listening either for this property or for all properties. ListenerList <PropertyChangedEventArgs> listeners = (ListenerList <PropertyChangedEventArgs>)dict[propertyName]; ListenerList <PropertyChangedEventArgs> genericListeners = (ListenerList <PropertyChangedEventArgs>)dict[String.Empty]; if (genericListeners == null) { if (listeners != null) { list = listeners; // only specific listeners } else { list = ListenerList.Empty; // no listeners at all } } else { if (listeners != null) { // there are both specific and generic listeners - // combine the two lists. list = new ListenerList <PropertyChangedEventArgs>(listeners.Count + genericListeners.Count); for (int i = 0, n = listeners.Count; i < n; ++i) { list.Add(listeners.GetListener(i)); } for (int i = 0, n = genericListeners.Count; i < n; ++i) { list.Add(genericListeners.GetListener(i)); } } else { list = genericListeners; // only generic listeners } } } else { // source has changed all properties. Notify all targets. // Use previously calculated combined list, if available. list = (ListenerList)dict[AllListenersKey]; if (list == null) { // make one pass to compute the size of the combined list. // This avoids expensive reallocations. int size = 0; foreach (DictionaryEntry de in dict) { Debug.Assert((String)de.Key != AllListenersKey, "special key should not appear"); size += ((ListenerList)de.Value).Count; } // create the combined list list = new ListenerList <PropertyChangedEventArgs>(size); // fill in the combined list foreach (DictionaryEntry de in dict) { ListenerList listeners = ((ListenerList)de.Value); for (int i = 0, n = listeners.Count; i < n; ++i) { list.Add(listeners.GetListener(i)); } } // save the result for future use (see below) _proposedAllListenersList = list; } } // mark the list "in use", even outside the read lock, // so that any writers will know not to modify it (they'll // modify a clone intead). list.BeginUse(); } // deliver the event, being sure to undo the effect of BeginUse(). try { DeliverEventToList(sender, args, list); } finally { list.EndUse(); } // if we calculated an AllListeners list, we should now try to store // it in the dictionary so it can be used in the future. This must be // done under a WriteLock - which is why we didn't do it immediately. if (_proposedAllListenersList == list) { using (WriteLock) { // test again, in case another thread changed _proposedAllListersList. if (_proposedAllListenersList == list) { HybridDictionary dict = (HybridDictionary)this[sender]; if (dict != null) { dict[AllListenersKey] = list; } _proposedAllListenersList = null; } // Another thread could have changed _proposedAllListersList // since we set it (earlier in this method), either // because it calculated a new one while handling a PropertyChanged(""), // or because it added/removed/purged a listener. // In that case, we will simply abandon our proposed list and we'll // have to compute it again the next time. But that only happens // if there's thread contention. It's not worth doing something // more complicated just for that case. } } }