次のクラスがあるとします。
public class Person : ReactiveObject, IEditableObject
{
private string name;
private string nameCopy;
public string Name
{
get { return this.name; }
set { this.RaiseAndSetIfChanged(ref this.name, value); }
}
public void BeginEdit()
{
this.nameCopy = this.name;
}
public void CancelEdit()
{
this.name = this.nameCopy;
}
public void EndEdit()
{
}
}
ここで、変更が にコミットさUnit
れるたびに「ティック」する監視可能なシーケンス (の) を作成したいとします。つまり、と の呼び出しとそれに続くの呼び出しの間に発生する への変更のみを考慮します。への呼び出し前の変更は無視され、シーケンスはティックされません。Name
Name
BeginEdit
EndEdit
CancelEdit
Rxでこれを行う方法を理解するのに苦労しています。BeginEdit
/EndEdit
呼び出しのウィンドウ中に変更が発生したかどうかを知るために、パイプラインのどこかで状態が必要になるようです。すべてにタイムスタンプを付けてタイムスタンプを比較できると思いますが、それは厄介なハックのようです。
Subject
専用の編集アクションを使用して、かなり近づきましたObservable.Merge
。
public class Person : ReactiveObject, IEditableObject
{
private readonly Subject<EditAction> editActions;
private readonly IObservable<Unit> changedDuringEdit;
private string name;
private string nameCopy;
public Person()
{
this.editActions = new Subject<EditAction>();
var nameChanged = this.ObservableForProperty(x => x.Name).Select(x => x.Value);
var editBeginning = this.editActions.Where(x => x == EditAction.Begin);
var editCommitted = this.editActions.Where(x => x == EditAction.End);
this.changedDuringEdit = nameChanged
.Buffer(editBeginning, _ => editCommitted)
.Where(x => x.Count > 0)
.Select(_ => Unit.Default);
}
public IObservable<Unit> ChangedDuringEdit
{
get { return this.changedDuringEdit; }
}
public string Name
{
get { return this.name; }
set { this.RaiseAndSetIfChanged(ref this.name, value); }
}
public void BeginEdit()
{
this.editActions.OnNext(EditAction.Begin);
this.nameCopy = this.name;
}
public void CancelEdit()
{
this.editActions.OnNext(EditAction.Cancel);
this.Name = this.nameCopy;
}
public void EndEdit()
{
this.editActions.OnNext(EditAction.End);
}
private enum EditAction
{
Begin,
Cancel,
End
}
}
ただし、いくつかの変更がキャンセルされてから 1 つがコミットされた場合、オブザーバブルはコミット時に数回ティックします (前のキャンセルごとに 1 回、コミットごとに 1 回)。List<Unit>
実際には必要のないを取得したという事実は言うまでもありません。ある意味では、これでも私のユース ケースは満足できますが、私の好奇心やコードの美的センスは満足できません。
Join
これをかなりエレガントに解決する必要があると思います:
var nameChanged = this.ObservableForProperty(x => x.Name).Select(_ => Unit.Default);
var editBeginning = this.editActions.Where(x => x == EditAction.Begin);
var editCommitted = this.editActions.Where(x => x == EditAction.End);
var editCancelled = this.editActions.Where(x => x == EditAction.Cancel);
var editCancelledOrCommitted = editCancelled.Merge(editCommitted);
this.changedDuringEdit = editBeginning
.Join(nameChanged, _ => editCancelledOrCommitted, _ => editCancelledOrCommitted, (editAction, _) => editAction == EditAction.End)
.Where(x => x)
.Select(_ => Unit.Default);
しかし、これもうまくいきません。にJoin
登録してeditCancelledOrCommitted
いないようです。理由がわかりません。
これをきれいに行う方法はありますか?