使用System.Reflection.Emit实现INotifyPropertyChanged时触发InvalidProgramException
问题分析与解决方案
System.InvalidProgramException 这类运行时错误,几乎都是手动生成的IL代码存在语法或逻辑错误导致的——因为Reflection.Emit不会像C#编译器那样帮你做语法校验,哪怕IL代码逻辑错了,编译阶段也不会报错,只有运行时才会触发异常。结合你的场景,问题大概率出在INotifyPropertyChanged事件的实现,或者属性set方法中调用OnPropertyChanged的IL逻辑里。
可能的错误点及检查方向
1. OnPropertyChanged方法的IL实现错误
OnPropertyChanged是触发通知的核心方法,常见错误包括:
- 事件null检查逻辑错误:比如没有先判断
PropertyChanged事件是否为null就直接调用Invoke,或者IL指令顺序颠倒(比如先调用Invoke再做null判断)。 PropertyChangedEventArgs实例创建错误:没有正确获取该类型的构造函数,或者传递参数时栈操作错误。- 委托调用错误:调用
PropertyChangedEventHandler.Invoke时,参数顺序或数量不对(需要传递this和PropertyChangedEventArgs实例)。
正确的OnPropertyChangedIL逻辑示例:
// 加载当前实例(this) ldarg.0 // 获取PropertyChanged事件的字段引用 ldfld class [System.ComponentModel]System.ComponentModel.PropertyChangedEventHandler YourDynamicClass::PropertyChanged // 如果事件为null,跳转到结束标签 brfalse.s SKIP_INVOKE // 重新加载this ldarg.0 // 加载传入的propertyName参数 ldarg.1 // 创建PropertyChangedEventArgs实例 newobj instance void [System.ComponentModel]System.ComponentModel.PropertyChangedEventArgs::.ctor(string) // 调用委托的Invoke方法 callvirt instance void [System.ComponentModel]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class [System.ComponentModel]System.ComponentModel.PropertyChangedEventArgs) SKIP_INVOKE: // 方法返回 ret
2. 属性set方法中调用OnPropertyChanged的IL错误
set方法里的常见问题:
- 通知调用顺序错误:还没把新值保存到字段就触发了通知,或者栈操作不平衡(比如加载了多余的参数,导致栈顶元素数量不符合方法调用要求)。
- 属性名字符串传递错误:比如没有正确加载属性名的字符串常量到栈上,或者传递了空字符串/错误的属性名。
正确的set方法IL逻辑示例:
// 加载this ldarg.0 // 加载传入的属性新值 ldarg.1 // 将新值保存到对应的字段 stfld object YourDynamicClass::_yourPropertyField // 加载this,准备调用OnPropertyChanged ldarg.0 // 加载属性名的字符串常量 ldstr "YourPropertyName" // 调用OnPropertyChanged方法 call instance void YourDynamicClass::OnPropertyChanged(string) // 方法返回 ret
3. INotifyPropertyChanged接口的实现错误
- 事件定义不规范:比如没有正确用
EventBuilder创建PropertyChanged事件,或者事件的add/remove方法逻辑错误(比如没有正确维护委托链)。 - 接口实现未正确关联:动态类没有正确实现
INotifyPropertyChanged接口,比如没有显式实现接口的事件成员。
排查建议
- 用反编译工具(如dnSpy)加载生成的动态程序集,查看动态类的IL代码,对比手动编写的
INotifyPropertyChanged实现,找出IL指令的差异。 - 重点检查栈操作:确保每个IL指令执行后栈的状态符合预期(比如调用方法前栈上的参数数量和类型必须匹配方法签名)。
- 分步验证:先单独测试动态类的属性赋值和通知触发逻辑,再绑定到Avalonia DataGrid,缩小问题范围。
内容的提问来源于stack exchange,提问作者Guillermo Espert
相关产品推荐
相关产品推荐

