在Getter中将PropertyChanged提升为自己的属性时的行为?

时间:2012-04-05 18:38:34

标签: c# wpf silverlight inotifypropertychanged

我这样做:

public string FirstName
{
    get
    {
        //Actual Code was doing something to change the value here
        this._FirstName = "Hello";
        this.OnPropertyChanged("FirstName");
        return this._FirstName;
    }
    set {
        if (this._FirstName == value)
            return;
        this._FirstName = value;
        this.OnPropertyChanged("FirstName");
    }
}

如您所见,我在FirstName属性中提升FirstName属性更改。我期待这将是一个无限循环,FirstName getter将继续调用自己。但奇怪的是 不会发生。

然后我尝试将raise属性更改放在BackgroundWorker

public string _FirstName { get; set; }
public string FirstName
{
    get
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += (sender, e) =>
            {
                Thread.Sleep(2000);
            };
        worker.RunWorkerCompleted += (sender, e) =>
            {
                this._FirstName = "Hi Hi";
                this.OnPropertyChanged("FirstName");
            };
        worker.RunWorkerAsync();
        return this._FirstName;
    }
    set {
        if (this._FirstName == value)
            return;
        this._FirstName = value;
        this.OnPropertyChanged("FirstName");
    }
}

你去,这次是无限循环。 但为什么在第一种情况下不会发生?

P.S。我不打算破解程序,只是我正在调试其他人程序,他们在这里做类似第一种情况。但该物业第二次没有被召唤。

更新: 测试案例2的StackTrace:

我通过将此属性绑定到TextBox并在FirstName getter上设置断点来测试它,我可以看到断点被无限次地命中。

at Person.get_FirstName()  

at RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)  

at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)  

at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)  

at RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)  

at RuntimePropertyInfo.GetValue(Object obj, Object[] index)  

at CLRPropertyListener.get_Value()  

at PropertyAccessPathStep.get_Value()  

at PropertyPathListener.ReconnectPath()  

at <>c__DisplayClass4.<BreakOnSharedType>b__3()  

at RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)  

at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder......

1 个答案:

答案 0 :(得分:2)

按照设计,PropertyChanged事件仅在属性值更改时才会引发。在getter中引发事件是不正确的,但它本身不会导致无限循环,甚至不使用属性的第二个定义(使用BackgroundWorker)。

然而,当消费者订阅你的事件时,在你的getter中提升PropertyChanged几乎肯定会导致无限循环,因为事件处理程序通常会做的第一件事就是得到通过吸气剂获得物业的新价值。

下面的典型示例即使在代码的第一个版本中也会导致无限循环:

Person person = new Person();
person.PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
{
    Console.WriteLine("New name: " + person.FirstName);
};
person.FirstName = "ABC";

我的假设是你得到一个无限循环,因为你有一个事件订阅,可以在第二次测试中访问getter。

修改:您正在将该属性绑定到TextBox;这解释了无限循环。在幕后,Silverlight(以及WPF)订阅绑定到的任何对象的PropertyChanged事件,其类型实现INotifyPropertyChanged;这就是它如何设法将状态更新从代码隐藏对象传播到UI控件。在其订阅的事件处理程序中,Silverlight显然需要检索属性的新值,并且它通过调用属性getter来实现,从而无限循环。

但是,我确实相信,如果您使用了属性的第一个定义(没有BackgroundWorker)并将对象绑定到TextBox,则会发生同样的事情。