在当前方法完成后,是否始终从Dispatcher.BeginInvoke()进行调用?

时间:2011-02-16 19:59:59

标签: c# silverlight

我有一个扩展方法,用于触发看起来像这样的PropertyChanged通知:

    public static TRet RaiseIfChanged<TObj, TRet>(this TObj target, Expression<Func<TObj, TRet>> property, TRet newValue)
        where TObj : AlantaViewModelBase
    {
        var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
        var propertyValue = propertyInfo.GetValue(target, null);

        if (!EqualityComparer<TRet>.Default.Equals((TRet)propertyValue, (TRet)newValue))
        {
            // Marshal this to the dispatcher so that the RaisePropertyChanged() notification happens after 
            // the value is actually set.
            Deployment.Current.Dispatcher.BeginInvoke(() => target.RaisePropertyChanged(propertyInfo.Name));
        }
        return newValue;
    }

它被这样使用:

    private bool isBusy;
    public bool IsBusy
    {
        get { return isBusy; }
        set { isBusy = this.RaiseIfChanged(p => p.IsBusy, value); }
    }

我的问题是我用Dispatcher.BeginInvoke()实际调用target.RaisePropertyChanged()。鉴于在扩展方法完成之前属性实际上不会发生变化,我需要确信在设置属性值之前,事件实际上不会被提升。我能否相信这种情况总是如此?或者是否有事件在属性发生变化之前被解雇?或者有更好的方法来解决这个问题吗?

4 个答案:

答案 0 :(得分:3)

你没有这样的保证。 RaisePropertyChanged()方法运行的确切时间取决于UI线程的状态。如果恰好准备好查看调用队列,以便调用目标在工作线程调用RaiseIfChanged的时间执行,并且工作线程失去处理器量,那么,是的,该调用将在工作者之前进行可以返回并设置属性值。不太可能,但良好的线程意图的道路充满了这样的道路杀伤力。

您必须在提升事件之前分配属性。干燥的测试和设置非常简单。通过使用PropertyInfo.SetValue()或通过引用传递支持变量,保持干燥但丑陋。就个人而言,我不会,使用反射来读取属性值比直接编码它要容易三个数量级。

答案 1 :(得分:2)

您的代码不保证执行顺序。也就是说,可以在设置属性之前发出事件信号。例如,在单处理器机器上,看到调度程序只是同步执行代码就不足为奇了。在这种情况下,事件将在从方法返回之前发出信号。

在多核或多处理器计算机上,所有投注均已关闭。我怀疑你会发现有时候事件会在房产改变之前发出信号,有时却不会发出信号。

安全的方法是:

set 
{
    var oldValue = isBusy;
    isBusy = value;
    this.RaiseIfChanged(oldValue, value); 
}

当然,这需要更改您的扩展方法。 。

答案 2 :(得分:0)

Dispatcher.BeginInvoke()计划一个新线程来运行lambda。一旦有处理器时间,该线程就会运行。我假设,RaisePropertyChanged()方法手动触发一个事件,在.NET中通常使用同步MulticastDelegate实现。如果是这种情况,事件处理程序可以在实际属性更改之前运行;它是否会取决于您无法控制的硬件细节,但缺点是,您无法保证。

你应该这样做的方法是存储原始值,执行赋值,然后如果值确实发生了变化则引发事件。 KISS:您可以重新定义RaiseIfChanged以取旧值而不是投影,或者您可以在触发事件之前在RaiseIfChanged内执行赋值。

答案 3 :(得分:0)

没有假设你可以决定何时执行。它将立即执行(在您从UI线程运行的情况下)或将来在任何点执行。