《框架设计指南》:属性变更通知事件

详细的告诉我c#中,属性 get和set的作用

在 C# 中,属性是一种特殊的类成员,它们将类字段公开为类的外部。属性中的 `get` 和 `set` 访问器定义了对该属性进行读取和写入时执行的代码。

具体来说,`get` 访问器返回属性的值,而 `set` 访问器设置属性的值。例如,我们可以定义一个名为 `Name` 的属性,它将字符串类型的字段 `_name` 公开给外部代码:

public string Name {
    get { return _name; }
    set { _name = value; }
}

在此属性内部,我们使用 `get` 访问器返回 `_name` 字段的值,使用 `set` 访问器为 `_name` 字段设置新的值。在外部代码中,我们可以读取和写入 `Name` 属性,例如:

var person = new Person();
person.Name = "Alice";
Console.WriteLine(person.Name);

第一行代码设置 `person` 对象的 `Name` 属性为 "Alice",而第二行代码将 `person` 对象的 `Name` 属性读取并将其值打印到控制台上。

总之,属性提供了一种方便、安全的方式来访问类字段,而 `get` 和 `set` 访问器则定义了这些访问操作的逻辑实现。

给出代码示例,set属性触发一个变更通知事件

public class Person {
    private string _name;

    public event EventHandler NameChanged;

    public string Name {
        get { return _name; }
        set {
            if (_name != value) {
                _name = value;
                OnNameChanged();
            }
        }
    }

    protected virtual void OnNameChanged() {
        NameChanged?.Invoke(this, EventArgs.Empty);
    }
}

在此示例中,Person 类包括一个名为 NameChanged 的事件和一个名为 Name 的属性。当 Name 属性被设置为新值时,我们检查新值是否与旧值不同。如果是这种情况,我们将字段 _name 更新为新值,并触发 OnNameChanged 方法。

OnNameChanged 方法是一个受保护的虚方法,它触发 NameChanged 事件。请注意,在调用事件之前,我们使用空合并运算符 ?. 来检查此事件是否有任何订阅者。这可以防止在没有订阅者的情况下触发空引用异常。

在外部代码中,我们可以添加一个事件处理程序来监听 NameChanged 事件,如下所示:

var person = new Person();
person.NameChanged += (sender, e) => {
    Console.WriteLine($"Name changed to: {((Person)sender).Name}");
};
person.Name = "Alice"; // 输出 "Name changed to: Alice"

这段代码创建了一个新的 Person 对象,并订阅了它的 NameChanged 事件。当我们将 person 对象的 Name 属性设置为新值 "Alice" 时,事件处理程序将输出消息 "Name changed to: Alice"。这表明事件已成功触发,并且包含了最新的属性值。

细节解释:

这段代码表示当名称更改时引发 `NameChanged` 事件。它是一个虚方法,因此可以在子类中进行重写,并且通常会在属性的 `set` 访问器中使用。让我们逐个检查代码:

protected virtual void OnNameChanged() {

这声明了一个名为 `OnNameChanged` 的受保护的虚方法。`protected` 关键字表示该方法只能从当前类或继承自该类的子类中访问。同时,`virtual` 关键字表示该方法可以被子类覆盖。

    NameChanged?.Invoke(this, EventArgs.Empty);

这里使用了 C# 6.0 中的 null 条件运算符 "?."。如果 `NameChanged` 不为空,则执行 `Invoke` 方法。`Invoke` 是一种用于调用委托的特殊方法。在这种情况下,它用于触发 `NameChanged` 事件。

这个委托需要两个参数:第一个参数是事件源对象即引发事件的对象;第二个参数是事件参数对象,表示事件相关的信息。在这种情况下,我们使用 `this` 关键字作为事件源对象,表示当前的对象,而使用预定义的 `EventArgs.Empty` 属性作为事件参数对象,因为该事件不需要传递任何有关事件的信息。

总之,这段代码向监听 `NameChanged` 事件的任何对象发出信号,表示名称已更改,以便它们可以采取相应的行动。通常,这些对象会注册一个处理程序方法,以便在事件发生时执行一些有用的操作。

person.NameChanged += (sender, e) => {
Console.WriteLine($"Name changed to: {((Person)sender).Name}");
}; 我不理解这段代码意思?是否有更容易理解的写法?

这段代码的意思是:当`person`对象发生 `NameChanged` 事件时,将执行一个匿名方法,该方法输出更改后的名称。

让我们逐个解释:

- `person.NameChanged += ...` 表示向 `person` 对象中 `NameChanged` 事件的委托列表中添加一个委托。换句话说,这段代码告诉应用程序监听 `NameChanged` 事件,并在事件发生时执行一些代码。
- `(sender, e) => { Console.WriteLine($"Name changed to: {((Person)sender).Name}"); }` 是一个 lambda 表达式,它表示一个匿名方法。这个方法接收两个参数:`sender` 和 `e`。在本例中,`sender` 参数包含引发事件的对象(即 `person`),而 `e` 参数则不使用。这个方法的主体是一个 `Console.WriteLine()` 语句,它显示名称已更改的消息。在这种情况下,`{((Person)sender).Name}` 引用了发出事件的对象的名称属性。

这段代码可以通过定义一个具有名称的常规方法来使其更容易理解,如下所示:

public void OnNameChangedEventHandler(object sender, EventArgs e) {
    // 将 sender 强制转换为 Person,以便能够访问 Name 属性
    Person person = (Person)sender;
    Console.WriteLine($"Name changed to: {person.Name}");
}


然后,在将事件处理程序添加到 `Person` 类之前,必须再创建一个具有名称的委托类型,如下所示:

public delegate void NameChangedEventHandler(object sender, EventArgs e);

现在,你可以将 `OnNameChangedEventHandler` 方法添加到该事件的委托列表中,如下所示:

public event NameChangedEventHandler NameChanged;

// ...

// 将处理程序方法添加到事件的委托列表中
person.NameChanged += OnNameChangedEventHandler;

这种方法更容易理解,因为它避免了使用 lambda 表达式和未经检查的强制转换。

你可能感兴趣的:(框架设计指南,c#,开发语言)