假设我有以下课程:
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
}
}
但是,如果取消了多个更改,然后提交了一个更改,则observable会在提交时多次滴答(每次事先取消一次,再次提交一次)。更不用说我得到一个我不需要的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
。
任何人都有任何想法如何干净利落地去做?
答案 0 :(得分:3)
我是这样做的:
IObservable<Unit> beginEditSignal = ...;
IObservable<Unit> commitSignal = ...;
IObservable<Unit> cancelEditSignal = ...;
IObservable<T> propertyChanges = ...;
// this will yield an array after each commit
// that has all of the changes for that commit.
// nothing will be yielded if the commit is canceled
// or if the changes occur before BeginEdit.
IObservable<T[]> commitedChanges = beginEditSignal
.Take(1)
.SelectMany(_ => propertyChanges
.TakeUntil(commitSignal)
.ToArray()
.Where(changeList => changeList.Length > 0)
.TakeUntil(cancelEditSignal))
.Repeat();
// if you really only want a `Unit` when something happens
IObservable<Unit> changeCommittedSignal = beginEditSignal
.Take(1)
.SelectMany(_ => propertyChanges
.TakeUntil(commitSignal)
.Count()
.Where(c => c > 0)
.Select(c => Unit.Default)
.TakeUntil(cancelEditSignal))
.Repeat();
答案 1 :(得分:1)
你有一个时间问题,我认为你还没有表达出来;你什么时候希望改变??
1)明显而明显的问题是你不知道是否会提交更改,那么为什么要提出这些更改。 IMO,这只留下选项2)。如果更改被取消,则不会引发任何事件。
我的下一个问题是,您是否希望每次提出更改?即。为过程
[Begin]-->[Name="fred"]-->[Name="bob"]-->[Commit]
提交提交时是否应该引发1或2个事件?由于您只是推送令牌类型Unit
,因此推送两个值似乎是多余的。现在这让我觉得你只想在执行Unit
时推送EndEdit()
值并且值已经改变。
这给我们带来了一个非常简单的实现:
public class Person : ReactiveObject, IEditableObject
{
private readonly ISubject<Unit> changedDuringEdit = new Subject<Unit>();
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()
{
if(!string.Equals(this.nameCopy, this.name))
{
changedDuringEdit.OnNext(Unit.Default);
}
}
public IObservable<Unit> ChangedDuringEdit
{
get { return this.changedDuringEdit.AsObservable(); }
}
}
这是你在找什么?如果没有,你能帮助我理解我所缺少的复杂性吗?如果是,那么我会热衷于将其充实,以便我不建议使用Subject
s: - )