using System; using System.Collections; using System.Collections.Generic; using System.Runtime.Serialization; namespace UniRx { public struct DictionaryAddEvent<TKey, TValue> : IEquatable<DictionaryAddEvent<TKey, TValue>> { 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<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(Value) << 2; } public bool Equals(DictionaryAddEvent<TKey, TValue> other) { return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(Value, other.Value); } } public struct DictionaryRemoveEvent<TKey, TValue> : IEquatable<DictionaryRemoveEvent<TKey, TValue>> { 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<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(Value) << 2; } public bool Equals(DictionaryRemoveEvent<TKey, TValue> other) { return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(Value, other.Value); } } public struct DictionaryReplaceEvent<TKey, TValue> : IEquatable<DictionaryReplaceEvent<TKey, TValue>> { 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<TKey>.Default.GetHashCode(Key) ^ EqualityComparer<TValue>.Default.GetHashCode(OldValue) << 2 ^ EqualityComparer<TValue>.Default.GetHashCode(NewValue) >> 2; } public bool Equals(DictionaryReplaceEvent<TKey, TValue> other) { return EqualityComparer<TKey>.Default.Equals(Key, other.Key) && EqualityComparer<TValue>.Default.Equals(OldValue, other.OldValue) && EqualityComparer<TValue>.Default.Equals(NewValue, other.NewValue); } } // IReadOnlyDictionary is from .NET 4.5 public interface IReadOnlyReactiveDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> { int Count { get; } TValue this[TKey index] { get; } bool ContainsKey(TKey key); bool TryGetValue(TKey key, out TValue value); IObservable<DictionaryAddEvent<TKey, TValue>> ObserveAdd(); IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false); IObservable<DictionaryRemoveEvent<TKey, TValue>> ObserveRemove(); IObservable<DictionaryReplaceEvent<TKey, TValue>> ObserveReplace(); IObservable<Unit> ObserveReset(); } public interface IReactiveDictionary<TKey, TValue> : IReadOnlyReactiveDictionary<TKey, TValue>, IDictionary<TKey, TValue> { } [Serializable] public class ReactiveDictionary<TKey, TValue> : IReactiveDictionary<TKey, TValue>, IDictionary<TKey, TValue>, IEnumerable, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, IDisposable #if !UNITY_METRO , ISerializable, IDeserializationCallback #endif { [NonSerialized] bool isDisposed = false; #if !UniRxLibrary [UnityEngine.SerializeField] #endif readonly Dictionary<TKey, TValue> inner; public ReactiveDictionary() { inner = new Dictionary<TKey, TValue>(); } public ReactiveDictionary(IEqualityComparer<TKey> comparer) { inner = new Dictionary<TKey, TValue>(comparer); } public ReactiveDictionary(Dictionary<TKey, TValue> 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<TKey, TValue>(key, oldValue, value)); } else { inner[key] = value; if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent<TKey, TValue>(key, value)); if (countChanged != null) countChanged.OnNext(Count); } } } public int Count { get { return inner.Count; } } public Dictionary<TKey, TValue>.KeyCollection Keys { get { return inner.Keys; } } public Dictionary<TKey, TValue>.ValueCollection Values { get { return inner.Values; } } public void Add(TKey key, TValue value) { inner.Add(key, value); if (dictionaryAdd != null) dictionaryAdd.OnNext(new DictionaryAddEvent<TKey, TValue>(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<TKey, TValue>(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<TKey, TValue>.Enumerator GetEnumerator() { return inner.GetEnumerator(); } void DisposeSubject<TSubject>(ref Subject<TSubject> 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<int> countChanged = null; public IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false) { if (isDisposed) return Observable.Empty<int>(); var subject = countChanged ?? (countChanged = new Subject<int>()); if (notifyCurrentCount) { return subject.StartWith(() => this.Count); } else { return subject; } } [NonSerialized] Subject<Unit> collectionReset = null; public IObservable<Unit> ObserveReset() { if (isDisposed) return Observable.Empty<Unit>(); return collectionReset ?? (collectionReset = new Subject<Unit>()); } [NonSerialized] Subject<DictionaryAddEvent<TKey, TValue>> dictionaryAdd = null; public IObservable<DictionaryAddEvent<TKey, TValue>> ObserveAdd() { if (isDisposed) return Observable.Empty<DictionaryAddEvent<TKey, TValue>>(); return dictionaryAdd ?? (dictionaryAdd = new Subject<DictionaryAddEvent<TKey, TValue>>()); } [NonSerialized] Subject<DictionaryRemoveEvent<TKey, TValue>> dictionaryRemove = null; public IObservable<DictionaryRemoveEvent<TKey, TValue>> ObserveRemove() { if (isDisposed) return Observable.Empty<DictionaryRemoveEvent<TKey, TValue>>(); return dictionaryRemove ?? (dictionaryRemove = new Subject<DictionaryRemoveEvent<TKey, TValue>>()); } [NonSerialized] Subject<DictionaryReplaceEvent<TKey, TValue>> dictionaryReplace = null; public IObservable<DictionaryReplaceEvent<TKey, TValue>> ObserveReplace() { if (isDisposed) return Observable.Empty<DictionaryReplaceEvent<TKey, TValue>>(); return dictionaryReplace ?? (dictionaryReplace = new Subject<DictionaryReplaceEvent<TKey, TValue>>()); } #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<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return ((ICollection<KeyValuePair<TKey, TValue>>)inner).IsReadOnly; } } ICollection<TKey> IDictionary<TKey, TValue>.Keys { get { return inner.Keys; } } ICollection<TValue> IDictionary<TKey, TValue>.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<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) { Add((TKey)item.Key, (TValue)item.Value); } bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) { return ((ICollection<KeyValuePair<TKey, TValue>>)inner).Contains(item); } void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { ((ICollection<KeyValuePair<TKey, TValue>>)inner).CopyTo(array, arrayIndex); } IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() { return ((ICollection<KeyValuePair<TKey, TValue>>)inner).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return inner.GetEnumerator(); } bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) { TValue v; if (TryGetValue(item.Key, out v)) { if (EqualityComparer<TValue>.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<TKey, TValue> ToReactiveDictionary<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) { return new ReactiveDictionary<TKey, TValue>(dictionary); } } }