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

動態編譯實戰(二):手寫 JSON 序列化器——反射版 vs 動態編譯版效能對決

     編輯:關於C#

以「複雜物件轉 JSON」為實戰題材,對比 純反射實作 與 動態編譯實作 的代碼結構與效能。動態編譯版針對每個型別生成專屬 ToJson 方法(字串拼接 + StringBuilder),首次編譯後快取 MethodInfo,後續調用仍走反射 Invoke(下一篇將優化為介面零反射)。

領域模型:包含巢狀物件、列舉、集合、字典、可空型別

public class User {
    public Guid UID { get; set; }
    public string Name { get; set; }
    public DateTime? Birthday { get; set; }
    public UserSex Sex { get; set; }           // 列舉
    public bool IsDeleted { get; set; }
    public List<DateTime> LoginHistory { get; set; }  // IEnumerable
    public UserInfo Info { get; set; }               // 巢狀物件
}

public class UserInfo {
    public string Address { get; set; }
    public Dictionary<string, string> Phone { get; set; } // IDictionary
    public int ZipCode { get; set; }
}
public enum UserSex { Male, Female }

抽象基類 JsonConverter:封裝基礎型別轉換

public abstract class JsonConverter {
    public abstract string ToJson(object obj);
    // FromBoolean, FromInt32, FromDateTime, FromGuid, FromEnum...
    // FromArray(IEnumerator), FromKeyValue(IEnumerable keys, IEnumerable values)
}

實作一:純反射版


public class JsonConverter_Reflection : JsonConverter {
    private static readonly Type ThisType = typeof(JsonConverter_Reflection);

    public override string ToJson(object obj) {
        if (obj == null) return "null";
        var type = Nullable.GetUnderlyingType(obj.GetType()) ?? obj.GetType();

        // System 基礎型別 → 反射呼叫 FromXxx
        if (type.Namespace == "System") {
            var m = ThisType.GetMethod("From" + type.Name);
            if (m != null) return (string)m.Invoke(this, new[] { obj });
        }
        // 列舉、字典、集合、巢狀物件遞迴...
    }
}

實作二:動態編譯版


public class JsonConverter_Dyncmp : JsonConverter {
    private static readonly Dictionary<Type, MethodInfo> Cache = new();
    private static readonly Type ThisType = typeof(JsonConverter_Dyncmp);

    public override string ToJson(object obj) {
        // ... 前置型別判斷同反射版 ...
        // 關鍵差異:動態生成專屬方法並快取
        if (!Cache.TryGetValue(type, out var method)) {
            string code = CreateCode(type); // 產生 StringBuilder 鏈式拼接代碼
            var ass = DynamicCompile_1.CompileAssembly(code, typeof(Json), typeof(StringBuilder), ...);
            method = ass.GetTypes()[0].GetMethods()[0];
            Cache[type] = method;
        }
        return (string)method.Invoke(null, new[] { obj }); // 仍用反射 Invoke
    }

    private string CreateCode(Type type) {
        // 產生類似:
        // return new StringBuilder()
        //   .Append("{").Append("'UID':").Append(Converter.FromGuid(obj.UID))
        //   .Append(",'Name':").Append(Converter.FromString(obj.Name))
        //   .Append(",'Sex':").Append(Converter.FromEnum(obj.Sex))
        //   .Append(",'LoginHistory':").Append(Converter.FromArray(obj.LoginHistory.GetEnumerator()))
        //   .Append(",'Info':").Append(ToJson_2(obj.Info))
        //   .Append("}").ToString();
    }
}

效能測試:10,000 次序列化同一物件

測試結果截圖

反射版:   ~150ms (穩定)
動態編譯: ~110ms (首次含編譯較慢,後續穩定)

此階段動態編譯版**略勝一籌**,但差距未顯著,因仍受限於 MethodInfo.Invoke 反射開銷。下一篇將引入介面機制徹底消除反射,效能再提升 10 倍。

輸出 JSON 對比(格式一致)


反射版:
{"UID":"1e10fe905f7d41d0bda2210abcb12349","Name":"blqw","Birthday":"1986-10-29 18:00:00","Sex":"Male","IsDeleted":false,"LoginHistory":["2013-08-09 08:00:00",...],"Info":{"Address":"廣東省廣州市","Phone":{"手機":"18688888888",...},"ZipCode":510000}}

動態編譯版:同上

原始碼結構

  • JsonConverter(抽象基類,基礎型別轉換)
  • JsonConverter_Reflection(反射遞迴實作)
  • JsonConverter_Dyncmp(動態編譯 + MethodInfo 快取)
  • DynamicCompile_1.CompileAssembly(底層編譯器)
  • Json.ToJson_1/ToJson_2(靜態門面)

完整代碼見原文。下一篇:介面 + 實例方法 = 零反射極速調用。

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