首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 3.0 模擬突變賦值:用擴展方法+反射實現對象屬性批量賦值

     編輯:關於C語言

背景:渴望優雅的屬性批量賦值

ASP.NET Core 路由配置中常見匿名類型傳參:


routes.MapRoute("Default", "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = "" });

這啟發了「突變賦值」想法:若 C# 支持 := 運算符,可一行完成多屬性賦值:


// 假想語法
form := new { Text = "Hello", Top = 100, Left = 200 };
// 等價於
form.Text = "Hello";
form.Top = 100;
form.Left = 200;

可惜 C# 無此運算符,也無語法宏。退而求其次:用擴展方法 + 反射模擬。

核心實現:Mutant.MAssign 擴展方法


public static class Mutant
{
    public static void MAssign(this object target, object source)
    {
        if (target == null || source == null) return;
        
        var sourceProps = source.GetType().GetProperties()
            .Where(p => p.CanRead);
            
        foreach (var sp in sourceProps)
        {
            var tp = target.GetType().GetProperty(sp.Name, sp.PropertyType);
            if (tp == null || !tp.CanWrite) continue;
            
            var value = sp.GetValue(source);
            tp.SetValue(target, value);
        }
    }
}

使用效果


// 傳統繁瑣方式
form.Text = "Hello World";
form.Top = 100;
form.Left = 200;

// 一行搞定
form.MAssign(new { Text = "Hello World", Top = 100, Left = 200 });

深度分析與優化方向

當前實現的局限

問題影響解決方向
反射性能開銷高頻調用有壓力緩存 PropertyInfo、使用表達式樹編譯委托、IL Emit
僅匹配同名同類型屬性類型不兼容(如 int/long)失效引入類型轉換器、AutoMapper 風格映射配置
無法處理字段、只讀屬性覆蓋面不全擴展 BindingFlags、支持私有 setter
無驗證、無異常處理靜默失敗難排查返回映射結果、收集錯誤信息

生產級替代方案:AutoMapper / Mapster


// AutoMapper 配置
var config = new MapperConfiguration(cfg => 
    cfg.CreateMap());
var mapper = config.CreateMapper();
var entity = mapper.Map(dto);

// Mapster(零配置、高性能)
var entity = dto.Adapt();

建議:簡單場景用 MAssign,複雜映射上成熟庫。

擴展:支持巢狀對象與集合映射


public static class AdvancedMutant
{
    public static void DeepAssign(this object target, object source, 
        HashSet visited = null)
    {
        visited ??= new HashSet();
        if (target == null || source == null || !visited.Add(source)) return;
        
        var sourceProps = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanRead);
            
        foreach (var sp in sourceProps)
        {
            var value = sp.GetValue(source);
            if (value == null) continue;
            
            var tp = target.GetType().GetProperty(sp.Name, sp.PropertyType);
            if (tp != null && tp.CanWrite)
            {
                // 同類型直接賦值
                if (tp.PropertyType == sp.PropertyType || 
                    tp.PropertyType.IsAssignableFrom(sp.PropertyType))
                {
                    tp.SetValue(target, value);
                }
                // 複雜類型遞歸映射
                else if (!tp.PropertyType.IsPrimitive && tp.PropertyType != typeof(string))
                {
                    var targetObj = tp.GetValue(target) ?? Activator.CreateInstance(tp.PropertyType);
                    targetObj.DeepAssign(value, visited);
                    tp.SetValue(target, targetObj);
                }
            }
        }
    }
}


總結

「突變賦值」思想雖無語法支持,但擴展方法 + 反射 + 匿名類型組合實現了極具表達力的屬性批量賦值模式。理解其本質是運行期屬性名匹配複製,有助於在適當場景(配置映射、DTO 轉換、測試數據構建)靈活應用,複雜場景轉向成熟映射庫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved