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

.NET框架原生按属性名自动映射POCO对象的方法咨询

当然有!.NET框架本身就提供了几种原生方案来实现POCO对象之间的属性自动映射,完全不需要依赖AutoMapper这类第三方库。下面我就给你详细拆解这些实用的方法:

.NET原生对象属性映射方案

1. 基于反射的手动映射(最直接的原生方式)

反射是.NET里最基础的元数据操作能力,和Database.SqlQuery的底层原理类似,都是通过属性名匹配来完成值的赋值。你可以通过反射遍历源对象的属性,再把对应的值同步到目标对象的同名属性上。

示例代码:

public static TTarget Map<TSource, TTarget>(TSource source) where TTarget : new()
{
    var target = new TTarget();
    var sourceProperties = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    var targetProperties = typeof(TTarget).GetProperties(BindingFlags.Public | BindingFlags.Instance);

    foreach (var sourceProp in sourceProperties)
    {
        // 匹配同名且同类型的可写属性
        var targetProp = targetProperties.FirstOrDefault(p => p.Name == sourceProp.Name && p.PropertyType == sourceProp.PropertyType);
        if (targetProp != null && targetProp.CanWrite)
        {
            var value = sourceProp.GetValue(source);
            targetProp.SetValue(target, value);
        }
    }
    return target;
}

调用方式很简单:

var sourceObj = new SourceClass { Id = 1, Name = "Test" };
var targetObj = Map<SourceClass, TargetClass>(sourceObj);

这种方式的优势是完全原生、零依赖;缺点是反射的性能相对偏低,如果是高频调用场景,建议提前缓存反射获取到的属性信息来优化速度。

2. 用表达式树构建高性能映射函数

如果觉得反射的性能达不到要求,可以用System.Linq.Expressions构建编译后的映射委托——第一次构建完成后,后续调用的性能几乎和手动写的硬编码映射函数一样。

示例代码:

public static class Mapper<TSource, TTarget> where TTarget : new()
{
    // 缓存编译后的映射委托
    private static readonly Func<TSource, TTarget> _mapFunc = CreateMapFunc();

    private static Func<TSource, TTarget> CreateMapFunc()
    {
        var sourceParam = Expression.Parameter(typeof(TSource), "source");
        var targetNew = Expression.New(typeof(TTarget));
        var propertyBindings = new List<MemberBinding>();

        foreach (var sourceProp in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var targetProp = typeof(TTarget).GetProperty(sourceProp.Name, BindingFlags.Public | BindingFlags.Instance);
            // 只处理同名同类型的可写属性
            if (targetProp != null && targetProp.CanWrite && targetProp.PropertyType == sourceProp.PropertyType)
            {
                var sourcePropAccess = Expression.Property(sourceParam, sourceProp);
                propertyBindings.Add(Expression.Bind(targetProp, sourcePropAccess));
            }
        }

        var memberInit = Expression.MemberInit(targetNew, propertyBindings);
        return Expression.Lambda<Func<TSource, TTarget>>(memberInit, sourceParam).Compile();
    }

    public static TTarget Map(TSource source)
    {
        return _mapFunc(source);
    }
}

使用时直接调用静态类的方法:

var targetObj = Mapper<SourceClass, TargetClass>.Map(sourceObj);

这种方式兼顾了原生性和高性能,非常适合需要频繁进行对象映射的场景。

3. 序列化/反序列化间接映射(简单粗暴的懒人方案)

利用.NET原生的序列化库(比如System.Text.Json或System.Xml.Serialization),把源对象序列化成JSON/XML这类中间格式,再反序列化成目标对象。这种方式完全不需要写任何映射逻辑,全靠序列化库自动匹配同名属性。

示例(使用System.Text.Json):

using System.Text.Json;

public static TTarget MapViaSerialization<TSource, TTarget>(TSource source)
{
    var jsonString = JsonSerializer.Serialize(source);
    return JsonSerializer.Deserialize<TTarget>(jsonString);
}

这种方式的优点是代码量极少,不用处理任何属性匹配细节;缺点是性能比前两种方式差一些,而且如果对象包含复杂类型或有自定义序列化需求,需要额外配置。但对于简单的POCO对象映射,这个方案足够省心。

4. 利用TypeDescriptor(适合数据绑定场景)

System.ComponentModel.TypeDescriptor是.NET原生的组件,它可以用来获取对象的属性描述符,进而完成属性值的复制。这种方式和反射类似,但在数据绑定相关的场景下会更友好。

示例代码:

using System.ComponentModel;

public static void MapUsingTypeDescriptor<TSource, TTarget>(TSource source, TTarget target)
{
    var sourceProps = TypeDescriptor.GetProperties(source);
    var targetProps = TypeDescriptor.GetProperties(target);

    foreach (PropertyDescriptor sourceProp in sourceProps)
    {
        PropertyDescriptor targetProp = targetProps[sourceProp.Name];
        if (targetProp != null && targetProp.PropertyType == sourceProp.PropertyType && !targetProp.IsReadOnly)
        {
            targetProp.SetValue(target, sourceProp.GetValue(source));
        }
    }
}

使用时需要先创建好目标对象:

var targetObj = new TargetClass();
MapUsingTypeDescriptor(sourceObj, targetObj);

总结一下:如果追求极致性能,优先选表达式树的方式;如果想省事儿,序列化反序列化或基础反射方案就够用;如果是数据绑定场景,TypeDescriptor会更适配。这些都是.NET框架原生提供的能力,完全不需要依赖第三方库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:21:21