PropertyChanged 的​​主体在哪里?

Where is body of PropertyChanged?

我想知道 PropertyChanged 的​​主体(实例)在哪里?

    private int age;
    public int Age
    {
        get { return age; }
        set 
        { 
            age = value;
            OnPropertyChanged("age");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }

在上面,"public event PropertyChangedEventHandler PropertyChanged" 只是一个声明,但在下一行之后突然 PropertyChanged 事件处理程序看起来已实例化,然后可以被调用。会发生什么?

要添加一些信息, 我使用 WPF 工作。 没有明确的事件订阅者代码。但是在xaml里面有线索。

<TextBox x:Name="txtAge" Text="{Binding Age}/>

所以,当xaml中有Binding时,某些东西会自动添加订阅者代码吗?

PropertyChanged 是一个事件 - 如果没有人订阅该事件,它将为空,否则它将为非空。您的 API 用户会这样订阅:

Person p = new Person(...);
p.PropertyChanged += MyHandler;

void MyHandler(object sender, PropertyChangedEventArgs args) { ... }

此外,您的空检查模式并非 100% 安全。它可能会在您检查后从非空变为空。

试试这个:

var callback = PropertyChanged;
if(callback != null) {
    callback(this, new PropertyChangedEventArgs(name));
}

另一个对象(通常)将订阅提供的事件处理程序 PropertyChanged

class 本身会在满足条件时调用它的 OnPropertyChanged 事件,然后检查它的 PropertyChanged 事件字段是否为空。如果此字段不为空,则表示事件的订阅者已对其进行初始化,因此可以调用它。

订阅看起来像这样:

InstanceOfYourClass.PropertyChanged += SomeFunctionThatHandlesIt;

private void SomeFunctionThatHandlesIt(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
    {
        DoSomething();
    }
}

这是一个场式事件。它有点像自动实现的 属性 - C# 编译器提供实现。 (如果语法更像是自动实现的属性,我会更喜欢它,但那是另一回事。)

来自 C# 5 规范的第 10.8.1 节:

When compiling a field-like event, the compiler automatically creates storage to hold the delegate, and creates accessors for the event that add or remove event handlers to the delegate field. The addition and removal operations are thread safe, and may (but are not required to) be done while holding the lock (§8.12) on the containing object for an instance event, or the type object (§7.6.10.6) for a static event.

Thus, an instance event declaration of the form:

class X
{
     public event D Ev;
}

will be compiled to something equivalent to:

class X
{
    private D __Ev;  // field to hold the delegate
    public event D Ev {
        add {
            /* add the delegate in a thread safe way */
        }
        remove {
            /* remove the delegate in a thread safe way */
        }
    }
}

Within the class X, references to Ev on the left-hand side of the += and –= operators cause the add and remove accessors to be invoked. All other references to Ev are compiled to reference the hidden field __Ev instead (§7.6.4). The name “__Ev” is arbitrary; the hidden field could have any name or no name at all.