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

如何在DTO类中自动实现持久化、克隆及XML序列化功能?

我完全懂你的痛点——重复写这些DTO的基础逻辑不仅无聊透顶,还容易因为手滑搞出各种低级bug。既然反射(性能顾虑)和常规代码生成(两次构建太麻烦)都不合心意,我给你几个更顺手的解决方案:


1. 使用C#源生成器(Source Generators)

这绝对是现在.NET生态里的最优解之一,完美解决你提到的两次构建问题——源生成器是在编译过程中直接运行的,基于你的DTO源代码生成逻辑,和项目一起编译,完全不需要额外的构建步骤。

实现思路:

  • 先定义一个自定义标记属性,比如[XmlDto],用来标记需要自动生成逻辑的DTO类;
  • 写一个源生成器,扫描项目中带有这个属性的类,分析它的公共属性(类型、名称等);
  • 自动生成LoadFromXml(XmlNode node)、SaveToXml(XmlNode node)和Clone()方法的原生代码,直接嵌入到项目编译流程里。

核心优势:

  • 性能拉满:生成的代码和你手写的完全一样,运行时没有任何反射开销;
  • 零额外步骤:编译时自动完成,不需要手动触发二次构建;
  • 高度可定制:可以针对不同属性类型(值类型、枚举、引用类型等)编写专属生成逻辑,比如处理空值、格式转换等。

简单的生成逻辑示例(伪代码):

// 源生成器里的核心代码片段
foreach (var property in targetDtoClass.Properties)
{
    // 生成SaveToXml的代码
    generatedCode.AppendLine($"node.SetAttributeValue(\"{property.Name}\", this.{property.Name});");
    
    // 生成LoadFromXml的代码
    generatedCode.AppendLine($"this.{property.Name} = ({property.Type})Convert.ChangeType(node.Attributes[\"{property.Name}\"]?.Value, typeof({property.Type}));");
    
    // 生成Clone的代码
    generatedCode.AppendLine($"newDto.{property.Name} = this.{property.Name};");
}

2. 接口+默认实现+表达式树缓存(C# 8.0+)

如果你不想折腾生成器这类工具,用C#的接口默认实现结合表达式树缓存,也能平衡复用性和性能——既避免重复写代码,又解决了纯反射的性能问题。

实现思路:

  • 定义一个接口IXmlSerializableDto,包含你需要的三个方法;
  • 在接口的默认实现里,用表达式树提前编译每个属性的Getter/Setter,缓存到静态字典里;
  • 所有DTO只要实现这个接口,就能自动获得这些方法,不用重复编写。

示例代码片段:

public interface IXmlSerializableDto
{
    void LoadFromXml(XmlNode node)
    {
        var dtoType = GetType();
        // 静态字典缓存属性Setter的编译表达式
        if (!PropertySetterCache.TryGetValue(dtoType, out var setters))
        {
            setters = dtoType.GetProperties()
                .ToDictionary(p => p.Name, BuildSetterExpression);
            PropertySetterCache[dtoType] = setters;
        }

        foreach (XmlAttribute attr in node.Attributes)
        {
            if (setters.TryGetValue(attr.Name, out var setter))
            {
                var value = Convert.ChangeType(attr.Value, setter.PropertyType);
                setter(this, value);
            }
        }
    }

    // SaveToXml和Clone的默认实现逻辑类似,用表达式树缓存Getter
    void SaveToXml(XmlNode node);
    IXmlSerializableDto Clone();

    // 静态缓存容器
    static Dictionary<Type, Dictionary<string, Action<object, object>>> PropertySetterCache { get; } = new();
    
    // 构建属性Setter的表达式树并编译
    static Action<object, object> BuildSetterExpression(PropertyInfo prop)
    {
        var instanceParam = Expression.Parameter(typeof(object));
        var valueParam = Expression.Parameter(typeof(object));
        var castInstance = Expression.Convert(instanceParam, prop.DeclaringType);
        var castValue = Expression.Convert(valueParam, prop.PropertyType);
        var setter = Expression.Assign(Expression.Property(castInstance, prop), castValue);
        return Expression.Lambda<Action<object, object>>(setter, instanceParam, valueParam).Compile();
    }
}

核心优势:

  • 零额外工具依赖:纯C#语言特性就能实现;
  • 性能接近原生:第一次调用会编译表达式树,后续调用直接执行编译后的IL代码,比反射快几个数量级;
  • 侵入性低:DTO只要实现接口就行,不需要修改其他逻辑。

3. T4模板(Text Template Transformation Toolkit)

如果源生成器对你来说有点门槛,T4模板也是个靠谱的选择——它能在设计时直接生成代码,你可以实时看到生成的文件,调试起来也方便,同样不需要二次构建。

实现思路:

  • 创建一个.tt模板文件,编写逻辑扫描你的DTO所在的命名空间;
  • 用Roslyn API分析DTO的源代码(或者直接引用编译后的程序集,更简单),获取属性信息;
  • 模板自动生成每个DTO的目标方法代码,保存后直接参与项目编译。

核心优势:

  • 可视化生成:生成的代码直接出现在项目里,你可以随时查看、修改(当然不建议手动改生成的代码);
  • 性能无损耗:生成的是原生代码,和手写逻辑完全一致;
  • 上手简单:语法接近常规C#,不需要深入理解编译器原理。

总结一下选择建议:

  • 追求现代.NET体验和极致性能:选源生成器;
  • 不想折腾工具,追求快速落地:选接口+表达式树缓存;
  • 喜欢能直观看到生成代码的方式:选T4模板。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:21:21