为什么通过XAML绑定可以修改C#的get-init属性?
为什么init属性可以被绑定修改
C# 中init访问器的赋值限制仅作用于编译阶段:仅允许在对象初始化器、所属类/派生类的构造函数、其他init访问器内部对该属性赋值,其他场景赋值会直接编译报错。但这个限制不会被写入运行时的类型元数据做强制校验,运行时通过反射、动态IL生成等方式完全可以绕过编译规则修改init属性的值。
WPF 绑定底层正是通过反射、表达式树动态生成赋值代码的方式实现属性写入,绕开了C#的编译期检查,因此可以直接修改TestRecord.Name的值,不需要新建TestRecord实例,和你测试得到的访问器调用顺序完全吻合。
另外你观察到的行为没有修改MyViewModel.TestRecordProp的引用,只是修改了该属性指向的TestRecord实例的内部属性,因此也不会触发TestRecordProp的init访问器调用。
阻止修改的实现方案
不是必须将控件设为只读,有多种可选方案:
- 成本最低的方案是在前端控件层面限制,将对应
TextBox的IsReadOnly属性设为True - 也可以修改
TestRecord的属性定义为纯只读,去掉init访问器:
public record TestRecord { public string Name { get; } public TestRecord(string name) => Name = name; }
这种方式下属性完全没有可写的访问器,WPF绑定无法写入值,自然无法修改
- 如果要保留完整的不可变特性,可以将
TestRecord声明为不可变类型,同时在ViewModel层面提供代理属性,在代理属性的setter中直接丢弃赋值请求,也能实现阻止修改的效果。
内容的提问来源于stack exchange,提问作者terborac
相关产品推荐
相关产品推荐

