如何让MODBUS驱动基类枚举派生类中定义的常量寄存器地址?
解决Modbus驱动基类枚举派生类寄存器常量的问题
问题背景
开发Modbus驱动时采用基类统一处理通用操作,派生类通过内部MODBUSRegisterAddresses类定义寄存器常量(相比枚举无需额外代码提取值)。但基类无法直接感知派生类的该内部类,原反射方案仅能访问基类自身的空嵌套类,无法获取派生类的寄存器地址集合。
可行方案
方案1:抽象方法+手动枚举(类型安全,推荐)
在基类定义抽象方法,让派生类返回自身的寄存器常量集合,基类直接调用该方法获取所有地址。
基类修改:
public abstract class A { // 定义抽象方法,由派生类提供所有寄存器地址 protected abstract IEnumerable<ushort> GetAllRegisterAddresses(); // 基类通用逻辑中可直接使用该方法 protected void ProcessAllRegisters() { var addresses = GetAllRegisterAddresses(); foreach (var addr in addresses) { // 执行针对单个寄存器的操作,比如读取 // var value = ReadRegister(device.SlaveAddress, addr); } } }
派生类实现:
public class B : A { protected override ushort MinRegister => MODBUSRegisterAddresses.GetTemperature; protected override ushort MaxRegister => MODBUSRegisterAddresses.SetHumidity; // 实现抽象方法,返回当前设备的所有寄存器地址 protected override IEnumerable<ushort> GetAllRegisterAddresses() { return new[] { MODBUSRegisterAddresses.GetTemperature, MODBUSRegisterAddresses.GetHumidity, MODBUSRegisterAddresses.SetTemperature, MODBUSRegisterAddresses.SetHumidity }; } private class MODBUSRegisterAddresses { public const ushort GetTemperature = 0; public const ushort GetHumidity = 10; public const ushort SetTemperature = 300; public const ushort SetHumidity = 310; } }
优点:完全类型安全,无反射开销,调试简单;缺点:派生类新增寄存器时需要手动更新集合。
方案2:改进反射逻辑(自动收集,减少重复)
调整基类的反射逻辑,通过当前实例的实际类型(this.GetType())查找派生类中的MODBUSRegisterAddresses嵌套类,再提取常量值。
基类修改:
public abstract class A { private readonly Lazy<IEnumerable<ushort>> _allRegisterAddresses = new Lazy<IEnumerable<ushort>>(() => { // 获取当前实例的实际派生类类型 var derivedType = this.GetType(); // 查找派生类中名为MODBUSRegisterAddresses的嵌套类 var registerClass = derivedType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic) .FirstOrDefault(t => t.Name == nameof(MODBUSRegisterAddresses)); if (registerClass == null) return Enumerable.Empty<ushort>(); // 提取所有public static的const ushort字段 return registerClass.GetFields(BindingFlags.Public | BindingFlags.Static) .Where(f => f.IsLiteral && !f.IsInitOnly && f.FieldType == typeof(ushort)) .Select(f => (ushort)f.GetValue(null)) .Distinct(); }); // 对外暴露所有寄存器地址的属性 protected IEnumerable<ushort> AllRegisterAddresses => _allRegisterAddresses.Value; }
派生类无需修改(保持原有MODBUSRegisterAddresses类即可)
优点:自动收集寄存器地址,派生类新增常量时无需额外修改;缺点:依赖反射有轻微性能开销,且依赖嵌套类名称约定(必须叫MODBUSRegisterAddresses)。
注意事项
- 方案2中,若派生类的
MODBUSRegisterAddresses是private修饰,需在GetNestedTypes中加入BindingFlags.NonPublic;若为internal,则无需额外调整。 - 若需要支持多个嵌套类或不同命名,可在基类定义抽象属性返回嵌套类类型,进一步灵活化。
内容的提问来源于stack exchange,提问作者djv
相关产品推荐
相关产品推荐

