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

使用System.Text.JSON实现含数组的类型安全类序列化的设计模式

是否要为序列化易用性牺牲类型安全?

不需要为了序列化易用性牺牲类型安全,有几种不依赖自定义JSON转换器和特性标记的设计方案可以兼顾两者:

方案1:引入数据传输对象(DTO)

  • 定义一个与Polynomial结构对应的只读DTO类,仅包含序列化所需的核心数据:
    public record PolynomialDto(int MaxOrder, Dictionary<int, double> CoefficientValues);
    
  • 在Polynomial类中添加双向转换方法,实现领域模型与DTO的映射:
    public class Polynomial
    {
        // 原有类型安全的代码保持不变
        
        public PolynomialDto ToDto()
        {
            var values = Coefficients.ToDictionary(c => c.Exponent, c => c.Value);
            return new PolynomialDto(MaxOrder, values);
        }
    
        public static Polynomial FromDto(PolynomialDto dto)
        {
            var polynomial = new Polynomial(dto.MaxOrder);
            foreach (var (exponent, value) in dto.CoefficientValues)
            {
                if (exponent >= 0 && exponent <= dto.MaxOrder)
                {
                    polynomial.Coefficients[exponent].Value = value;
                }
                // 可按需处理非法指数,比如抛出异常或忽略
            }
            return polynomial;
        }
    }
    
  • 序列化/反序列化时只操作DTO对象,完全隔离领域模型与序列化逻辑,既保留类型安全,又避免技术耦合。

方案2:重构内部存储与访问逻辑

  • 将Coefficients改为私有字段,对外提供只读访问接口,同时新增一个专用于序列化的属性:
    public class Polynomial
    {
        private readonly PolynomialCoefficient[] _coefficients;
    
        public Polynomial(int maxOrder)
        {
            MaxOrder = maxOrder;
            _coefficients = new PolynomialCoefficient[maxOrder + 1];
            for (int i = 0; i <= maxOrder; i++)
            {
                _coefficients[i] = new PolynomialCoefficient(i);
            }
        }
    
        public int MaxOrder { get; }
        // 对外暴露只读的系数列表,保证类型安全
        public IReadOnlyList<PolynomialCoefficient> Coefficients => _coefficients;
        // 序列化专用属性,内部自动验证指数合法性
        public Dictionary<int, double> CoefficientValues
        {
            get => _coefficients.ToDictionary(c => c.Exponent, c => c.Value);
            set
            {
                if (value == null) return;
                foreach (var (exponent, val) in value)
                {
                    if (exponent >= 0 && exponent <= MaxOrder)
                    {
                        _coefficients[exponent].Value = val;
                    }
                }
            }
        }
    }
    
  • 这种方式下,外部无法修改系数数组的结构,类型安全不受影响;序列化时通过CoefficientValues完成数据读写,且内部会自动过滤非法指数,避免出现数组与MaxOrder不兼容的问题。

方案3:用工厂模式接管反序列化流程

  • 保留Polynomial的只读特性,将反序列化逻辑转移到独立的工厂类中:
    public static class PolynomialFactory
    {
        public static Polynomial CreateFromSerializedData(int maxOrder, Dictionary<int, double> coefficientValues)
        {
            var polynomial = new Polynomial(maxOrder);
            foreach (var (exponent, value) in coefficientValues)
            {
                if (exponent < 0 || exponent > maxOrder)
                {
                    throw new ArgumentOutOfRangeException(nameof(exponent), $"指数必须在0到{maxOrder}之间");
                }
                polynomial.Coefficients[exponent].Value = value;
            }
            return polynomial;
        }
    }
    
  • 序列化时仅输出MaxOrder和系数键值对,反序列化时先将JSON转为基础类型(如匿名对象或字典),再通过工厂方法创建合法的Polynomial实例,全程不破坏原有模型的类型安全设计。

内容的提问来源于stack exchange,提问作者tmren

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 07:47:05