在 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` 访问器则定义了这些访问操作的逻辑实现。
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 表达式和未经检查的强制转换。