如何在父类Parent中监听子类Child的属性变更并触发处理方法?
这个需求很实用——在父类里统一处理子类的属性变更,能省不少重复代码。下面给你几种可行的实现方式,你可以根据自己的场景来选:
方案一:用INotifyPropertyChanged(推荐,符合.NET规范)
这是.NET里处理属性变更的标准玩法,让父类实现这个接口,然后子类在属性setter里触发变更事件,父类监听事件就能调用MyHandler了。
先修改Parent类:
using System.ComponentModel; using System.Runtime.CompilerServices; public class Parent : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public Parent() { // 监听属性变更事件,触发时自动调用MyHandler PropertyChanged += (sender, args) => MyHandler(args); } // 提供给子类调用的触发方法,CallerMemberName能自动获取当前属性名 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public void MyHandler(PropertyChangedEventArgs args) { Console.WriteLine($"属性 {args.PropertyName} 已经更新"); // 这里可以编写你的业务处理逻辑 } }
然后子类Child只需要在属性setter里调用父类的OnPropertyChanged就行:
public class Child : Parent { private string _name; public string Name { get => _name; set { if (_name != value) { _name = value; // 触发属性变更事件,父类会自动监听到 OnPropertyChanged(); } } } }
这种方式的好处是代码清晰、性能优异,完全符合.NET的设计习惯。你可以在父类的说明文档里明确告诉用户:继承这个类时,属性setter里记得调用OnPropertyChanged,甚至可以给个代码模板,用户照着写非常方便。
方案二:用动态代理(无需修改子类代码)
如果不想让用户写额外的setter逻辑,那可以用动态代理来拦截子类的属性赋值操作。这里需要用到Castle DynamicProxy第三方库,不过要注意性能和实例创建方式的变化。
首先安装Castle.Core NuGet包,然后修改Parent类:
using Castle.DynamicProxy; using System.ComponentModel; public class Parent : IInterceptor { public void Intercept(IInvocation invocation) { // 判断当前调用是不是属性的set方法 if (invocation.Method.Name.StartsWith("set_")) { // 先执行原本的赋值操作 invocation.Proceed(); // 提取属性名(去掉方法名前的"set_"前缀) var propertyName = invocation.Method.Name.Substring(4); // 触发处理方法 MyHandler(new PropertyChangedEventArgs(propertyName)); } else { // 非set方法直接执行原逻辑 invocation.Proceed(); } } public void MyHandler(PropertyChangedEventArgs args) { Console.WriteLine($"属性 {args.PropertyName} 发生了变更"); } }
子类可以完全保持原样:
public class Child : Parent { public string Name { get; set; } }
但使用时需要通过代理创建实例,不能直接new:
var proxyGenerator = new ProxyGenerator(); Child child = proxyGenerator.CreateClassProxy<Child>(); child.Name = "小明"; // 此时会触发Parent类中的MyHandler
这种方式的优点是子类完全不用修改,用户使用起来省心;缺点是要引入第三方库,实例创建方式有变化,性能也比第一种方案稍差,适合对性能要求不高的场景。
总结
如果能让子类配合编写少量setter逻辑,方案一绝对是首选,规范又好用;如果完全不想改动子类代码,可以考虑方案二,但要权衡好性能和使用成本。
内容的提问来源于stack exchange,提问作者TamerM
相关产品推荐
相关产品推荐

