使用StructLayout时实现INotifyPropertyChanged报错,如何解决?
问题描述
原代码
namespace Program { [StructLayout(LayoutKind.Explicit)] public class Block : INotifyPropertyChanged { [FieldOffset(0)] private byte[] _byteData; public Block() { _byteData = new byte[10]; } public byte[] ByteData { get => _byteData; set => _byteData = value; } public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
编译错误
首次编译报错:
Error: error CS0625: 'Block.PropertyChanged': instance field in types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute
尝试给PropertyChanged事件添加FieldOffset属性:
[FieldOffset(1)] public event PropertyChangedEventHandler PropertyChanged;
出现新错误:
Error: error CS0592: Attribute 'FieldOffset' is not valid on this declaration type. It is only valid on 'field' declarations.
疑问
- 如何解决上述错误?
- 使用
StructLayout时是否可以实现INotifyPropertyChanged接口?
解决方案
问题本质
当类标记[StructLayout(LayoutKind.Explicit)]时,所有实例字段必须显式指定FieldOffset。但事件默认会自动生成私有后备字段,这个自动生成的字段没有FieldOffset,所以触发第一个错误;而FieldOffset属性只能作用于字段,不能直接加在事件声明上,这就是第二个错误的原因。具体修复代码
手动声明事件的后备字段并指定FieldOffset,然后显式实现事件的add和remove访问器:
namespace Program { [StructLayout(LayoutKind.Explicit)] public class Block : INotifyPropertyChanged { [FieldOffset(0)] private byte[] _byteData; // 手动声明事件的后备字段,指定合法的FieldOffset [FieldOffset(8)] private PropertyChangedEventHandler _propertyChanged; public Block() { _byteData = new byte[10]; } public byte[] ByteData { get => _byteData; set { _byteData = value; NotifyPropertyChanged(); } } // 显式实现事件的访问器,操作手动声明的后备字段 public event PropertyChangedEventHandler PropertyChanged { add => _propertyChanged += value; remove => _propertyChanged -= value; } protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
注意:FieldOffset的值需要根据字段类型的内存占用合理设置,避免内存重叠。比如示例中_byteData是引用类型,64位系统占8字节,所以把_propertyChanged的偏移设为8,确保不重叠。
- 接口实现可行性
使用StructLayout时完全可以实现INotifyPropertyChanged接口,只要手动处理事件的后备字段并正确指定FieldOffset即可。
内容的提问来源于stack exchange,提问作者Praburaj
相关产品推荐
相关产品推荐

