ObservableCollections.ObservableDictionaryをReadonlyにしたい

# これは何

`UniRx.ReactiveDictionary`から`R3`+`ObservableCollections.ObservableDictionary`に乗り換えるメモです。


# ObservableCollectionsとは

https://qiita.com/toRisouP/items/c42769dd23a91b1b4762

過去にこちらでまとめているので参考にしてください。
R3と連携する方法は[このあたり](https://qiita.com/toRisouP/items/c42769dd23a91b1b4762#r3%E3%81%AEobservable%E3%81%A8%E9%80%A3%E6%90%BA%E3%81%99%E3%82%8B)を参考にして下さい。

# ReadOnlyなReactiveなDictionaryが欲しい

`UniRx.ReactiveDictionary`の代替として`R3`+`ObservableCollections.ObservableDictionary`が使えるのですが、ReadOnly化して公開することができません。（`UniRx.IReactiveDictionary`に相当するものが無い）

そのため無理やりですがReadOnlyに変換してみます。


```cs:ReadOnlyObservableDictionary
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ObservableCollections;
using R3;

namespace ObservableCollectionsExt
{
    public class ReadOnlyObservableDictionary<TKey, TValue>
        : IReadOnlyObservableDictionary<TKey, TValue> where TKey : notnull
    {
        private ObservableDictionary<TKey, TValue> observableDictionary;

        public ReadOnlyObservableDictionary(ObservableDictionary<TKey, TValue> observableDictionary)
        {
            this.observableDictionary = observableDictionary;
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => observableDictionary.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        public int Count => observableDictionary.Count;

        public bool ContainsKey(TKey key) => observableDictionary.ContainsKey(key);

        public bool TryGetValue(TKey key, out TValue value) => observableDictionary.TryGetValue(key, out value);

        public TValue this[TKey key] => observableDictionary[key];

        public IEnumerable<TKey> Keys => observableDictionary.Select(x => x.Key);
        public IEnumerable<TValue> Values => observableDictionary.Select(x => x.Value);

        public Observable<CollectionAddEvent<KeyValuePair<TKey, TValue>>> ObserveAdd()
            => observableDictionary.ObserveAdd();

        public Observable<int> ObserveCountChanged(bool notifyCurrentCount = false)
            => observableDictionary.ObserveCountChanged();

        public Observable<CollectionRemoveEvent<KeyValuePair<TKey, TValue>>> ObserveRemove()
            => observableDictionary.ObserveRemove();

        public Observable<CollectionReplaceEvent<KeyValuePair<TKey, TValue>>> ObserveReplace()
            => observableDictionary.ObserveReplace();

        public Observable<Unit> ObserveReset()
            => observableDictionary.ObserveReset();
    }

    public interface IReadOnlyObservableDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
    {
        Observable<CollectionAddEvent<KeyValuePair<TKey, TValue>>> ObserveAdd();
        Observable<int> ObserveCountChanged(bool notifyCurrentCount = false);
        Observable<CollectionRemoveEvent<KeyValuePair<TKey, TValue>>> ObserveRemove();
        Observable<CollectionReplaceEvent<KeyValuePair<TKey, TValue>>> ObserveReplace();
        Observable<Unit> ObserveReset();
    }

    public static class ObservableCollectionsExt
    {
        public static IReadOnlyObservableDictionary<TKey, TValue> ToReadOnlyObservableDictionary<TKey, TValue>(
            this ObservableDictionary<TKey, TValue> observableDictionary)
            where TKey : notnull
            => new ReadOnlyObservableDictionary<TKey, TValue>(observableDictionary);
    }
}
```

```cs:使い方
using ObservableCollections;
using ObservableCollectionsExt;
using R3;

// オリジナル
var original = new ObservableDictionary<int, string>();

// ReadOnly化
var readOnlyDic = original.ToReadOnlyObservableDictionary();

// オリジナルと同じ様に参照可能
readOnlyDic.ObserveAdd().Subscribe(x=> Console.WriteLine(x));
readOnlyDic.ObserveRemove().Subscribe(x=> Console.WriteLine(x));
readOnlyDic.ObserveReplace().Subscribe(x=> Console.WriteLine(x));
readOnlyDic.ObserveCountChanged().Subscribe(Console.WriteLine);

original[0] = "1";
original[0] = "2";
original[1] = "3";
original[4] = "4";
original.Remove(0);
original.Clear();
original[1] = "aa";

Console.WriteLine(readOnlyDic[1]);

```

# 感想

* ReadonlyなObservableDictionaryは欲しいから用意してみた
* ただし変換時にクラスをインスタンス化するためコストあり
    * 使うならキャッシュして使い回すようにしましょう