我编写了一个类,其属性类型为TProperty
,类型为Action<TProperty>
的事件会在该属性发生更改时触发。 TProperty
通常是枚举类型,但这无关紧要。
我想创建一个带签名的方法
bool WaitUntilPropertyIs(int TimeoutMs, IEnumerable<TProperty> AllowedValues)
阻止调用它的线程,直到属性更改为AllowedValues
中的值。当然如果在属性被AllowedValues
中的某个属性被调用时调用WaitUntilPropertyIs,则应该没有阻塞。 WaitUntilPropertyIs
最多等待TimeoutMs
,如果超出超时(正常AutoResetEvent.Wait
语义),则返回false。
我愿意使用Reactive Extensions或传统的同步构造。
答案 0 :(得分:4)
对于这种情况,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;
}
}
我不知道你的确切要求,所以考虑修改上面的代码以防止竞争条件。
答案 1 :(得分:4)
在Rx + ReactiveUI中,这可以通过以下方式完成:
someObject.ObservableForProperty(x => x.SomeProperty)
.Where(x => x.Value == "Something")
.First();
如果我们想添加超时+一个表示该属性实际设置的bool,我们可以这样做:
bool valueIsSet = someObject.ObservableForProperty(x => x.SomeProperty)
.Where(x => x.Value == "Something")
.Select(x => true)
.Timeout(TimeSpan.FromSeconds(5), Observable.Return(false))
.First();
答案 2 :(得分:1)
我决定将所有这些功能包装在一个类中,以便我可以在其他地方重用它。这段代码可以解决问题。
在构造函数中,传入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
}
答案 3 :(得分:0)
'Waiter'类和列表式容器应该这样做。锁定列表访问和属性setter / event /具有相同Critical Section的任何内容。服务员类只需要AllowedValues和一个让调用者等待的事件,(好吧,可能是'bool check(IEnumerable newAllowedValues)'帮助者)。
那应该是它。在WaitUntilPropertyIs()中,获取CS并首先检查属性值以查看它是否可以立即释放CS并立即返回,如果不是,则调用者必须等待。创建一个服务员,在传递的AllowedValues中复制,将服务员添加到列表中,释放CS并等待传递超时的事件。当事件等待返回时,重新获取CS,从列表中删除服务员,dispose()它,释放CS并从事件等待调用返回true / false。
在setter / Action / whatever中,获取CS,迭代列表,与新的AllowedValues比较,在新值满足范围的任何服务员上触发任何事件,然后释放CS。
RGDS, 马丁
答案 4 :(得分:0)
将该属性建模为Behavior
(Rx中的类,以及在一段时间内更改的值的反应式编程中的一般概念)。