我对通知属性有种执念
- 要兼容低版本
- 写起来短小精悍
- 最好无依赖
- 哪有那么好的事情!
必备代码
所有衍生的方式都将基于此操作,你可以封装到类中继承
【资料图】
public event PropertyChangedEventHandler PropertyChanged;protected void RaisePropertyChanged(string propertyName){PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));}
原始方式
这种方式,
- 你必须要接受一个不需要的后台变量
- 硬编码的名字写法你也可以使用nameof进行操作,但需要注意的是nameof是编译时的动作,不要让混淆器来捣乱
private int myVar;public int MyProperty{ get { return myVar; } set { myVar = value; //通知绑定系统这个名字的属性有变化 RaisePropertyChanged("MyProperty"); }}
使用[CallerMemberName]
隐藏属性名字的主动编写
在RaisePropertyChanged
的属性上加上[CallerMemberName]
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
写法就变成了
private int myVar;public int MyProperty{ get { return myVar; } set { myVar = value; //在编译时将会为第一个参数自动传入 "MyProperty" RaisePropertyChanged(); //如果没有.NET版本没有[CallerMemberName],也可以使用表达式树来操作,这个操作没有硬编码,是运行时操作 // RaisePropertyChanged(()=>MyProperty); }}
进一步干掉后台变量
通过基类将[CallerMemberName]的名字保存到后台统一字典来进行变量值的管理这也是我目前使用的方法
public int MyProperty{ get { return GetProperty(); } set { SetProperty(value); }}
优化成一行
public int MyProperty { get => GetProperty(); set => SetProperty(value); }
使用Emit动态生成
你的属性这样写那个virtual表明了它的派生类可以覆盖它
public class MyViewModel : ViewModelBase{ public virtual int MyProperty { get; set; } }
现在由Emit为你动态创建一个派生类在实例化后,返回给你基类,看似什么都没有发生,你的属性却得到了通知能力动态创建的代码类似下方所示:
public class NullViewModel_EXTEND : MyViewModel{private int myVar;public override int MyProperty{get { return myVar; }set{myVar = value; RaisePropertyChanged("MyProperty");}}}
由于是动态创建的,可能调试会有些麻烦此操作和js的代理一样,可以为原有对象注入拦截代码,只不过c#这个操作有亿点点麻烦实现参考: https://www.codewrecks.com/post/old/2008/08/implement-inotifypropertychanged-with-dynamic-code-generation/
使用后台线程轮训检查
这是我觉得最离谱的实现,但它能跑,怕什么!地址: https://github.com/michael-damatov/shuriken看起来还挺流畅
还有一种没太具体了解的方式
在maui早期mvu模式演示上见过猜测这个State的参数里面也包含了CallerMemberName然后重写ToString,重写隐式转换来达到使用起来像正常属性的操作
public class MyPage : View {readonly State clickCount = new State (1);public MyPage() {Body = () => new VStack {new Text (() => $"Click Count: {clickCount}"),new Button("Update Text", () => {clickCount.Value++;}};}}
下面2种属于无需动手的操作
使用Fody/PropertyChanged插件
此插件会在你的代码编译后对属性进行自动通知注入你只需要像正常编写属性一样
使用Source Generator自动生成
与Fody类似,可以在编译时为你自动生成通知代码