声明接口类型变量后无法访问特定实现类属性的技术咨询
TCP消息解码方案改进建议
当前问题根源
你声明的IMesTelegram接口仅定义了Header属性,所以即便变量实际指向子类实例,编译阶段也只能访问接口暴露的成员,无法直接调用子类特有的属性。整体继承结构的思路是合理的,但解码逻辑的类型处理需要调整。
改进方案
方案1:在switch分支中直接使用子类类型
在每个case分支里先创建子类类型的变量,完成字段赋值后再赋值给IMesTelegram变量,这样就能直接访问子类属性:
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) { MesTelegramHeader header = new(); header.Id = input.ReadUnsignedShortLE(); header.SenderId = input.ReadUnsignedShortLE(); header.ReceiverId = input.ReadUnsignedShortLE(); header.MessageType = input.ReadByte(); header.DataLength = input.ReadUnsignedShortLE(); IMesTelegram mesTelegram = null; switch (header.Id) { case 123: var versionTelegram = new MesVersionInfoTelegram(); // 直接访问子类属性读取输入 versionTelegram.InterfaceVersionMajor = input.ReadUnsignedShortLE(); versionTelegram.InterfaceVersionMinor = input.ReadUnsignedShortLE(); versionTelegram.StringLength = input.ReadUnsignedShortLE(); // 注意:实际项目中需根据StringLength和协议指定编码处理字符串 versionTelegram.SoftwareVersion = input.ReadCharSequence(versionTelegram.StringLength, Encoding.ASCII).ToString(); mesTelegram = versionTelegram; break; case 456: var ackTelegram = new MesAckTelegram(); ackTelegram.AckReject = input.ReadByte(); ackTelegram.MessageId = input.ReadUnsignedShortLE(); ackTelegram.ResponseId = input.ReadUnsignedShortLE(); ackTelegram.ResponseTimeOut = input.ReadUnsignedIntLE(); mesTelegram = ackTelegram; break; default: // 处理未知消息类型,可选择创建默认实例或抛出异常 mesTelegram = new MesTelegram(); break; } mesTelegram.Header = header; output.Add(mesTelegram); }
方案2:将解码逻辑封装到子类(更解耦)
把基类MesTelegram改为抽象类,定义抽象的DecodeBody方法,让子类各自实现自身字段的解码逻辑,主Decode方法只负责处理头部和创建实例:
// 修改基类为抽象类,增加抽象解码方法 internal abstract class MesTelegram : IMesTelegram { public MesTelegramHeader Header { get; set; } public abstract void DecodeBody(IByteBuffer input); } // 子类实现专属解码逻辑 internal class MesAckTelegram : MesTelegram { public byte AckReject { get;set; } public ushort MessageId { get; set; } public ushort ResponseId { get; set; } public uint ResponseTimeOut { get; set; } public override void DecodeBody(IByteBuffer input) { AckReject = input.ReadByte(); MessageId = input.ReadUnsignedShortLE(); ResponseId = input.ReadUnsignedShortLE(); ResponseTimeOut = input.ReadUnsignedIntLE(); } } internal class MesVersionInfoTelegram : MesTelegram { public ushort InterfaceVersionMajor { get; set; } public ushort InterfaceVersionMinor { get; set; } public ushort StringLength { get; set; } public string SoftwareVersion { get; set; } public override void DecodeBody(IByteBuffer input) { InterfaceVersionMajor = input.ReadUnsignedShortLE(); InterfaceVersionMinor = input.ReadUnsignedShortLE(); StringLength = input.ReadUnsignedShortLE(); SoftwareVersion = input.ReadCharSequence(StringLength, Encoding.ASCII).ToString(); } } // 简化后的主Decode方法 protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output) { MesTelegramHeader header = new(); header.Id = input.ReadUnsignedShortLE(); header.SenderId = input.ReadUnsignedShortLE(); header.ReceiverId = input.ReadUnsignedShortLE(); header.MessageType = input.ReadByte(); header.DataLength = input.ReadUnsignedShortLE(); MesTelegram mesTelegram = null; switch (header.Id) { case 123: mesTelegram = new MesVersionInfoTelegram(); break; case 456: mesTelegram = new MesAckTelegram(); break; default: // 若基类是抽象类,可创建一个默认子类处理未知消息,或抛出异常 throw new NotSupportedException($"未知消息ID:{header.Id}"); } mesTelegram.Header = header; mesTelegram.DecodeBody(input); output.Add(mesTelegram); }
后续转换为其他系统消息的建议
如果需要根据电报类型转换为其他系统消息,推荐两种方式:
- 策略模式:为每种电报类型定义对应的转换器,用字典存储
消息ID-转换器的映射,根据header.Id直接获取转换器处理,避免大量if/else判断。 - 访问者模式:定义消息访问者接口,每个电报子类实现
Accept方法,访问者实现不同的转换逻辑,适合后续扩展多种转换需求的场景。
内容的提问来源于stack exchange,提问作者f.lechleitner
相关产品推荐
相关产品推荐

