You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

声明接口类型变量后无法访问特定实现类属性的技术咨询

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 02:43:34