我写了一个类,它具有类型为TProperty
的属性和类型为Action<TProperty>
的事件,每当该属性更改时触发。TProperty
是典型的枚举类型,但这并不重要。
bool WaitUntilPropertyIs(int TimeoutMs, IEnumerable<TProperty> AllowedValues)
阻塞调用它的线程,直到属性更改为AllowedValues
中的值。当然,如果WaitUntilPropertyIs被调用时,属性已经是AllowedValues
之一,不应该有阻塞。WaitUntilPropertyIs
应该最多等待TimeoutMs
,如果超时超过(正常的AutoResetEvent.Wait
语义),则返回false。
我对使用响应式扩展或传统同步结构持开放态度。
Rx对于这种场景来说是多余的。您可以通过ManualResetEvent
或ManualResetEventSlim
进行操作。您可以按照以下方式对解决方案进行模式设置:
public event Action<TProperty> MyEvent;
public TProperty Prop { get; private set; }
bool WaitUntilPropertyIs(int timeout, IEnumerable<TProperty> allowedValues)
{
var gotAllowed = new ManualResetEventSlim(false);
Action<int> handler = item =>
{
if (allowedValues.Contains(item)) gotAllowed.Set();
};
try
{
MyEvent += handler;
return allowedValues.Contains(Prop) || gotAllowed.Wait(timeout);
}
finally
{
MyEvent -= handler;
}
}
我不知道你的确切需求,所以考虑修改上面的代码来防止竞争条件
在Rx + ReactiveUI中,这是通过:
someObject.ObservableForProperty(x => x.SomeProperty)
.Where(x => x.Value == "Something")
.First();
如果我们想添加一个timeout +一个bool值,表示该属性已经被设置,我们可以这样做:
bool valueIsSet = someObject.ObservableForProperty(x => x.SomeProperty)
.Where(x => x.Value == "Something")
.Select(x => true)
.Timeout(TimeSpan.FromSeconds(5), Observable.Return(false))
.First();
我决定将所有这些功能包装在一个类中,以便我可以在其他地方重用它。下面的代码完成了这个任务。
在构造函数中,传入CurrentValueFunc,它必须根据需要返回被观察变量的当前值,传入IsValueAcceptableFunc,如果当前值是可接受的,它必须返回true。
您需要确保ValueWatcher。ValueUpdated在值发生变化时被调用。
public class ValueWatcher<TValue> : IDisposable
{
ManualResetEvent _ev = new ManualResetEvent(false);
Func<TValue, bool> _isValueAcceptableFunc;
public ValueWatcher(Func<TValue> CurrentValueFunc, Func<TValue, bool> IsValueAcceptableFunc)
{
_isValueAcceptableFunc = IsValueAcceptableFunc;
ValueUpdated(CurrentValueFunc.Invoke());
}
public void ValueUpdated(TValue Value)
{
if (_isValueAcceptableFunc.Invoke(Value))
_ev.Set();
else
_ev.Reset();
}
public bool Wait()
{
return _ev.WaitOne();
}
public bool Wait(int TimeoutMs)
{
return _ev.WaitOne(TimeoutMs);
}
public bool Wait(TimeSpan ts)
{
return _ev.WaitOne(ts);
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
}
void Dispose(bool Disposing)
{
if (Disposing)
{
_ev.Dispose();
}
}
#endregion
}
应该由一个'Waiter'类和一个列表样式的容器来完成。锁定列表访问和具有相同临界区的属性setter/event等。服务员类只需要AllowedValues和一个事件供调用者等待(好吧,可能是一个'bool check(IEnumerable newAllowedValues)' helper)。
应该是这样。在WaitUntilPropertyIs()中,获取CS并首先检查属性值,看看它是否可以释放CS并立即返回,如果不能,调用者必须等待。创建一个服务员,复制传入的AllowedValues,将服务员添加到列表中,释放CS并等待传入超时的事件。当事件等待返回时,重新获取CS,从列表中删除服务员,dispose()它,释放CS并从事件等待调用返回true/false。
在setter/Action/whatever中,获取CS,迭代列表,与新的AllowedValues进行比较,在新值满足范围的任何侍者上触发任何事件,然后释放CS。
祝好,马丁
将该属性建模为Behavior
(来自Rx的类,以及响应式编程中用于一段时间内变化的值的一般概念)