2022.12.30
DynamicData のついて調査したまとめです。
きっかけは GitHub の Explore Repositories の表示です。GitHubスターは 1.5k あり(ReactiveProperty で 800スター)一定の人気がありそうですが、日本語の紹介記事は全く見つかりませんでした。
以下に Github/README.md の内容を整理します。
DynamicData のコンセプトは、コレクションを直接管理することなく、操作の連鎖によってデータを宣言的に操作/整形すること らしいです。以下は原文。
The concept behind using dynamic data is you maintain a data source (either
SourceCache<TObject, TKey>
orSourceList<TObject>
), then chain together various combinations of operators to declaratively manipulate and shape the data without the need to directly manage any collection.
一意なIDを持っている場合 SourceCache<TObject, TKey>
を使いましょう。
理由1:SourceCache<TObject, TKey>
は Dictionary ベースなので要素が重複しません。 一方 SourceList<TObject>
は重複を許しており、要素新の概念がありません。
理由2:SourceCache<TObject, TKey>
には Key をベースにした多くの操作が存在し、相対的にパフォーマンスが高いです。
作成
var myInts = new SourceList<int>();
myInts.AddRange(Enumerable.Range(0, 10));
読み取り専用として公開
IObservableList<int> readonlyInts = myInts.AsObservableList();
監視可能な変更リストの取得
IObservable<IChangeSet<int>> myIntsObservable = myInts.Connect();
作成
var myCache = new SourceCache<TObject, TKey>(t => key);
読み取り専用として公開
IObservableCache<TObject, TKey> readonlyCache = myCache.AsObservableCache();
監視可能な変更リストの取得
IObservable<IChangeSet<TObject, TKey>> myCacheObservable = myCache.Connect();
IObservable<IChangeSet<T>>
を作成する主な方法を紹介します。
-
ISourceList<T>
もしくはISourceCache<T,K>
から作成する(一般的な作成方法)var myObservableChangeSet = myDynamicDataSource.Connect();
-
IObservable<T>
から作成するIObservable<T> myObservable; IObservable<IEnumerable<T>> myObservable; var myObservableChangeSet = myObservable.ToObservableChangeSet(t => t.key);
上記の例の問題は、監視可能な変更セットの内部バッキング キャッシュのサイズが永遠に大きくなることです。 対策方法が 2つ 用意されています。
// 対策1:キャッシュの有効期間を指定する var myConnection = myObservable.ToObservableChangeSet(t => t.key, expireAfter: item => TimeSpan.FromHours(1));
// 対策2:キャッシュの最大サイズを指定する var myConnection = myObservable.ToObservableChangeSet(t => t.key, limitSizeTo:10000);
★2つを指定することも可能らしいです。きっと OR 条件で思われますが未確認です。
-
System.Collections.ObjectModel.ObservableCollection<T>
から作成する※スレッド セーフではないため、UI スレッドでのみ動作する単純なクエリに対してのみ推奨されます。
var oc = new ObservableCollection<T>(); IObservable<IChangeSet<T>> list = oc.ToObservableChangeSet(); IObservable<IChangeSet<TObject, TKey>> cache = oc.ToObservableChangeSet(x => x.Key);
-
System.ComponentModel.BindingList<T>
から作成する※スレッド セーフではないため、UI スレッドでのみ動作する単純なクエリに対してのみ推奨されます。
var bl = new BindingList<T>(); IObservable<IChangeSet<T>> list = bl.ToObservableChangeSet(); IObservable<IChangeSet<TObject, TKey>> cache = bl.ToObservableChangeSet(x => x.Key);
-
静的クラス
ObservableChangeSet
から作成するIObservable<IChangeSet<int>> observableList = ObservableChangeSet.Create<int>( source => { // some code to load data and subscribe var loader = myService.LoadMyDataObservable().Subscribe(source.Add); var subscriber = myService.GetMySubscriptionsObservable().Subscribe(source.Add); return new CompositeDisposable(loader, subscriber); });
IObservable<IChangeSet<T>>
でできること。
各操作によるシーケンスは常にキャッシュを正確に反映します。つまり、追加/更新/削除は常に伝播されます。
以下に README に載っていた操作を紹介します。全てではありません。
Filter
(Rx のWhere
)Sort
GroupOn
Transform
(Rx のSelect
)TransformMany
(Rx のSelectMany
)- Aggregation (
Count
,Max
,Min
,StdDev
,Avg
) - Logical Operators (
And
,Or
,Xor
,Except
) DisposeMany
DistinctValues
(ソースから指定プロパティを選択します)- Virtualisation (
Virtualise
,Page
) ★理解していません。
以下は実装例です。
// out修飾子でパラメータを渡すためプロパティは使用不可
ReadOnlyObservableCollection<TradeProxy> list;
var myTradeCache = new SourceCache<Trade, long>(trade => trade.Id);
var disposable = myTradeCache.Connect()
.Filter(trade =>trade.Status is TradeStatus.Live)
.Transform(trade => new TradeProxy(trade))
.Sort(SortExpressionComparer<TradeProxy>.Descending(t => t.Timestamp))
.ObserveOnDispatcher()
.Bind(out list)
.DisposeMany()
.Subscribe();
// 1. 指定プロパティの変化時に、その値を返します
IObservable<TKey> changed1 = cacheList.Connect().WhenValueChanged(p => p.Key);
// 2. 指定プロパティの変化時に、対象インスタンスとプロパティ値のペアを返します
IObservable<PropertyValue<TObject, TKey>> changed2 = cacheList.Connect().WhenPropertyChanged(p => p.Key);
// 3. いづれかの変更通知が発行された場合にインスタンスを返します
IObservable<TObject?> changed3 = cacheList.Connect().WhenAnyPropertyChanged();
★大事そうな内容が書いてるけど英文から内容を理解できませんでした。ソフトを触りながら内容を確認したい。
reactivemarbles/DynamicData: Reactive collections based on Rx.Net 公式
Dynamic Data | dynamic data brings the power of reactive (rx) to collections github/README.md によると古くてダメらしいです。(alas it is hopelessly out of date.)