using System; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; namespace UniRx { public struct DictionaryAddEvent : IEquatable> { public TKey Key { get; private set; } public TValue Value { get; private set; } public DictionaryAddEvent(TKey key, TValue value) : this() { Key = key; Value = value; } public override string ToString() { return string.Format("Key:{0} Value:{1}", Key, Value); } public override int GetHashCode() { return EqualityComparer.Default.GetHashCode(Key) ^ EqualityComparer.Default.GetHashCode(Value) << 2; } public bool Equals(DictionaryAddEvent other) { return EqualityComparer.Default.Equals(Key, other.Key) && EqualityComparer.Default.Equals(Value, other.Value); } } public struct DictionaryRemoveEvent : IEquatable> { public TKey Key { get; private set; } public TValue Value { get; private set; } public DictionaryRemoveEvent(TKey key, TValue value) : this() { Key = key; Value = value; } public override string ToString() { return string.Format("Key:{0} Value:{1}", Key, Value); } public override int GetHashCode() { return EqualityComparer.Default.GetHashCode(Key) ^ EqualityComparer.Default.GetHashCode(Value) << 2; } public bool Equals(DictionaryRemoveEvent other) { return EqualityComparer.Default.Equals(Key, other.Key) && EqualityComparer.Default.Equals(Value, other.Value); } } public struct DictionaryReplaceEvent : IEquatable> { public TKey Key { get; private set; } public TValue OldValue { get; private set; } public TValue NewValue { get; private set; } public DictionaryReplaceEvent(TKey key, TValue oldValue, TValue newValue) : this() { Key = key; OldValue = oldValue; NewValue = newValue; } public override string ToString() { return string.Format("Key:{0} OldValue:{1} NewValue:{2}", Key, OldValue, NewValue); } public override int GetHashCode() { return EqualityComparer.Default.GetHashCode(Key) ^ EqualityComparer.Default.GetHashCode(OldValue) << 2 ^ EqualityComparer.Default.GetHashCode(NewValue) >> 2; } public bool Equals(DictionaryReplaceEvent other) { return EqualityComparer.Default.Equals(Key, other.Key) && EqualityComparer.Default.Equals(OldValue, other.OldValue) && EqualityComparer.Default.Equals(NewValue, other.NewValue); } } // IReadOnlyDictionary is from .NET 4.5 public interface IReadOnlyReactiveDictionary : IEnumerable> { int Count { get; } TValue this[TKey index] { get; } bool ContainsKey(TKey key); bool TryGetValue(TKey key, out TValue value); IObservable> ObserveAdd(); IObservable ObserveCountChanged(bool notifyCurrentCount = false); IObservable> ObserveRemove(); IObservable> ObserveReplace(); IObservable ObserveReset(); } public interface IReactiveDictionary : IReadOnlyReactiveDictionary, IDictionary { } [Serializable] public class ReactiveDictionary : IReactiveDictionary, IDictionary, IEnumerable, ICollection>, IEnumerable>, IDictionary, IDisposable #if !UNITY_METRO , ISerializable, IDeserializationCallback #endif { [NonSerialized] bool isDisposed = false; #if !UniRxLibrary [UnityEngine.SerializeField] #endif readonly Dictionary inner; public ReactiveDictionary() { inner = new Dictionary(); } public ReactiveDictionary(IEqualityComparer comparer) { inner = new Dictionary(comparer); } public ReactiveDictionary(Dictionary innerDictionary) { inner = innerDictionary; } public TValue this[TKey key] { get { return inner[key]; } set { TValue oldValue; if (TryGetValue(key, out oldValue)) { inner[key] = value; if (dictionaryReplace != null) dictionaryReplace.OnNext(new DictionaryReplaceEvent(key, oldValue, value)); } else { inner[key] = value; if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent(key, value)); if (countChanged != null) countChanged.OnNext(Count); } } } public int Count { get { return inner.Count; } } public Dictionary.KeyCollection Keys { get { return inner.Keys; } } public Dictionary.ValueCollection Values { get { return inner.Values; } } public void Add(TKey key, TValue value) { inner.Add(key, value); if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent(key, value)); if (countChanged != null) countChanged.OnNext(Count); } public void Clear() { var beforeCount = Count; inner.Clear(); if (collectionReset != null) collectionReset.OnNext(Unit.Default); if (beforeCount > 0) { if (countChanged != null) countChanged.OnNext(Count); } } public bool Remove(TKey key) { TValue oldValue; if (inner.TryGetValue(key, out oldValue)) { var isSuccessRemove = inner.Remove(key); if (isSuccessRemove) { if (dictionaryRemove != null) dictionaryRemove.OnNext(new DictionaryRemoveEvent(key, oldValue)); if (countChanged != null) countChanged.OnNext(Count); } return isSuccessRemove; } else { return false; } } public bool ContainsKey(TKey key) { return inner.ContainsKey(key); } public bool TryGetValue(TKey key, out TValue value) { return inner.TryGetValue(key, out value); } public Dictionary.Enumerator GetEnumerator() { return inner.GetEnumerator(); } void DisposeSubject(ref Subject subject) { if (subject != null) { try { subject.OnCompleted(); } finally { subject.Dispose(); subject = null; } } } #region IDisposable Support private bool disposedValue = false; protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { DisposeSubject(ref countChanged); DisposeSubject(ref collectionReset); DisposeSubject(ref dictionaryAdd); DisposeSubject(ref dictionaryRemove); DisposeSubject(ref dictionaryReplace); } disposedValue = true; } } public void Dispose() { Dispose(true); } #endregion #region Observe [NonSerialized] Subject countChanged = null; public IObservable ObserveCountChanged(bool notifyCurrentCount = false) { if (isDisposed) return Observable.Empty(); var subject = countChanged ?? (countChanged = new Subject()); if (notifyCurrentCount) { return subject.StartWith(() => this.Count); } else { return subject; } } [NonSerialized] Subject collectionReset = null; public IObservable ObserveReset() { if (isDisposed) return Observable.Empty(); return collectionReset ?? (collectionReset = new Subject()); } [NonSerialized] Subject> dictionaryAdd = null; public IObservable> ObserveAdd() { if (isDisposed) return Observable.Empty>(); return dictionaryAdd ?? (dictionaryAdd = new Subject>()); } [NonSerialized] Subject> dictionaryRemove = null; public IObservable> ObserveRemove() { if (isDisposed) return Observable.Empty>(); return dictionaryRemove ?? (dictionaryRemove = new Subject>()); } [NonSerialized] Subject> dictionaryReplace = null; public IObservable> ObserveReplace() { if (isDisposed) return Observable.Empty>(); return dictionaryReplace ?? (dictionaryReplace = new Subject>()); } #endregion #region implement explicit object IDictionary.this[object key] { get { return this[(TKey)key]; } set { this[(TKey)key] = (TValue)value; } } bool IDictionary.IsFixedSize { get { return ((IDictionary)inner).IsFixedSize; } } bool IDictionary.IsReadOnly { get { return ((IDictionary)inner).IsReadOnly; } } bool ICollection.IsSynchronized { get { return ((IDictionary)inner).IsSynchronized; } } ICollection IDictionary.Keys { get { return ((IDictionary)inner).Keys; } } object ICollection.SyncRoot { get { return ((IDictionary)inner).SyncRoot; } } ICollection IDictionary.Values { get { return ((IDictionary)inner).Values; } } bool ICollection>.IsReadOnly { get { return ((ICollection>)inner).IsReadOnly; } } ICollection IDictionary.Keys { get { return inner.Keys; } } ICollection IDictionary.Values { get { return inner.Values; } } void IDictionary.Add(object key, object value) { Add((TKey)key, (TValue)value); } bool IDictionary.Contains(object key) { return ((IDictionary)inner).Contains(key); } void ICollection.CopyTo(Array array, int index) { ((IDictionary)inner).CopyTo(array, index); } #if !UNITY_METRO public void GetObjectData(SerializationInfo info, StreamingContext context) { ((ISerializable)inner).GetObjectData(info, context); } public void OnDeserialization(object sender) { ((IDeserializationCallback)inner).OnDeserialization(sender); } #endif void IDictionary.Remove(object key) { Remove((TKey)key); } void ICollection>.Add(KeyValuePair item) { Add((TKey)item.Key, (TValue)item.Value); } bool ICollection>.Contains(KeyValuePair item) { return ((ICollection>)inner).Contains(item); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { ((ICollection>)inner).CopyTo(array, arrayIndex); } IEnumerator> IEnumerable>.GetEnumerator() { return ((ICollection>)inner).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return inner.GetEnumerator(); } bool ICollection>.Remove(KeyValuePair item) { TValue v; if (TryGetValue(item.Key, out v)) { if (EqualityComparer.Default.Equals(v, item.Value)) { Remove(item.Key); return true; } } return false; } IDictionaryEnumerator IDictionary.GetEnumerator() { return ((IDictionary)inner).GetEnumerator(); } #endregion } public static partial class ReactiveDictionaryExtensions { public static ReactiveDictionary ToReactiveDictionary(this Dictionary dictionary) { return new ReactiveDictionary(dictionary); } } }