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

C#子类修饰返回类型:实现父类负责自身属性的分层序列化

可行的C#实现方案

针对你要实现父类负责自身属性序列化、子类仅处理新增属性的需求,同时避免手动复制父类属性、不用Dictionary<string, object>、规避TypeNameHandling.Auto风险,这里提供几种符合C#特性的方案:


方案1:通过填充方法分离父类/子类序列化逻辑

核心思路是让父类提供一个填充自身属性的方法,子类重写该方法时先调用父类逻辑,再处理自己的新增属性,无需手动复制父类属性。

代码示例:

public class Parent
{
    public string Foo { get; set; }

    // 父类负责填充自身属性到传入的数据对象
    protected virtual void PopulateSerializableData(Parent.Data data)
    {
        data.Foo = Foo;
    }

    public virtual Parent.Data GetSerializableData()
    {
        var data = new Parent.Data();
        PopulateSerializableData(data);
        return data;
    }

    public class Data
    {
        public string Foo { get; set; }
    }
}

public class Child : Parent
{
    public string Bar { get; set; }

    // 子类先调用父类填充逻辑,再处理自己的属性
    protected override void PopulateSerializableData(Parent.Data data)
    {
        base.PopulateSerializableData(data);
        if (data is Child.Data childData)
        {
            childData.Bar = Bar;
        }
    }

    public override Parent.Data GetSerializableData()
    {
        var data = new Child.Data();
        PopulateSerializableData(data);
        return data;
    }

    public new class Data : Parent.Data
    {
        public string Bar { get; set; }
    }
}

优势:

  • 完全遵循“父类管自己,子类管新增”的职责分离
  • 子类无需手动复制父类属性,避免遗漏或重复代码
  • 类型安全,没有强制类型转换风险

方案2:使用C# 9+记录类型(Record)的with表达式

C# 9引入的记录类型支持with表达式,可以轻松基于父类的序列化结果扩展子类数据,代码极度简洁。

代码示例:

// 父类数据记录
public record ParentData(string Foo);

public class Parent
{
    public string Foo { get; set; }

    public virtual ParentData GetSerializableData()
    {
        return new ParentData(Foo);
    }
}

// 子类数据记录,继承父类记录
public record ChildData(string Foo, string Bar) : ParentData(Foo);

public class Child : Parent
{
    public string Bar { get; set; }

    public override ParentData GetSerializableData()
    {
        // 基于父类的序列化结果直接扩展子类数据,无需手动复制父类属性
        var parentData = base.GetSerializableData();
        return new ChildData(parentData.Foo, Bar);
        
        // 或者用更简洁的with表达式(需将ChildData的Bar设为init属性)
        // return parentData with { Bar = Bar };
    }
}

优势:

  • 代码极简,接近TypeScript的扩展语法体验
  • 记录类型默认不可变,避免序列化过程中的意外修改
  • 类型安全,编译期检查属性完整性

方案3:利用AutoMapper自动映射属性

如果实体类属性较多,手动填充效率低,可以用AutoMapper自动处理父类到子类数据的映射,无需手动编写复制逻辑。

代码示例:

首先安装AutoMapper NuGet包,然后配置映射:

// 全局配置映射(可放在启动逻辑中)
var mapperConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Parent, Parent.Data>();
    // 子类映射自动继承父类的映射规则
    cfg.CreateMap<Child, Child.Data>().IncludeBase<Parent, Parent.Data>();
});
IMapper mapper = mapperConfig.CreateMapper();

// 父类实现
public class Parent
{
    public string Foo { get; set; }

    public virtual Parent.Data GetSerializableData()
    {
        return mapper.Map<Parent.Data>(this);
    }

    public class Data
    {
        public string Foo { get; set; }
    }
}

// 子类实现
public class Child : Parent
{
    public string Bar { get; set; }

    public override Parent.Data GetSerializableData()
    {
        return mapper.Map<Child.Data>(this);
    }

    public new class Data : Parent.Data
    {
        public string Bar { get; set; }
    }
}

优势:

  • 无需手动编写任何属性复制逻辑,AutoMapper自动处理
  • 支持复杂对象嵌套映射,适合大型项目
  • 映射规则集中管理,便于维护

方案4:自定义JsonConverter直接序列化实体类

如果不想额外定义数据类,直接序列化实体类本身,可以通过自定义JsonConverter实现父类/子类的序列化职责分离,同时避免TypeNameHandling.Auto的类型转换风险。

代码示例:

public class ParentConverter : JsonConverter<Parent>
{
    public override void WriteJson(JsonWriter writer, Parent value, JsonSerializer serializer)
    {
        var jsonObj = new JObject();
        // 父类负责序列化自身属性
        jsonObj.Add("Foo", value.Foo);
        
        // 如果是子类,序列化子类新增属性
        if (value is Child child)
        {
            jsonObj.Add("Bar", child.Bar);
        }
        
        jsonObj.WriteTo(writer);
    }

    public override Parent ReadJson(JsonReader reader, Type objectType, Parent existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var jsonObj = JObject.Load(reader);
        // 根据属性判断是父类还是子类,反序列化对应类型
        if (jsonObj.ContainsKey("Bar"))
        {
            return new Child
            {
                Foo = jsonObj["Foo"].Value<string>(),
                Bar = jsonObj["Bar"].Value<string>()
            };
        }
        return new Parent
        {
            Foo = jsonObj["Foo"].Value<string>()
        };
    }
}

// 使用方式
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ParentConverter());
string serialized = JsonConvert.SerializeObject(new Child { Foo = "demo", Bar = "child" }, settings);

优势:

  • 无需额外定义数据类,直接序列化业务实体
  • 完全控制序列化逻辑,避免TypeNameHandling.Auto带来的安全隐患和类型转换问题
  • 反序列化时自动识别类型,无需手动指定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 07:54:55