以「複雜物件轉 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 }
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();
}
}

反射版: ~150ms (穩定) 動態編譯: ~110ms (首次含編譯較慢,後續穩定)
此階段動態編譯版**略勝一籌**,但差距未顯著,因仍受限於 MethodInfo.Invoke 反射開銷。下一篇將引入介面機制徹底消除反射,效能再提升 10 倍。
反射版:
{"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(靜態門面)完整代碼見原文。下一篇:介面 + 實例方法 = 零反射極速調用。