使用Mono.Cecil給訊息類自動新增Attribute
阿新 • • 發佈:2022-12-08
C#的序列化庫很多通過註解Attribute來實現,比如Protobuf-net,Messagepack等。如果手動添加註解,會比較麻煩,尤其在切換序列化庫的時候,需要更改對應的註解。
這裡實現了一個使用Mono.Cecil來自動添加註解的類,當程式編譯後,會修改對應的dll來實現效果。
遇到的坑:IDE對於IL級別的錯誤沒有充分清晰的錯誤提示,需要自己折騰一會兒,去花時間排查問題。【比如註解應該加在Property上的,加在了Field上 /doge】
public class CecilUser { public static void Work(string fileName) { using (var module = ModuleDefinition.ReadModule(fileName, new ReaderParameters { ReadWrite = true })) { // Modify the assembly foreach (TypeDefinition type in module.Types) { if (!type.IsPublic) continue; if (!type.IsClass) continue; if (ContainsAttribute(type.CustomAttributes, nameof(MessagePackObjectAttribute))) continue; ModifyType(module, type); } module.Write(); // Write to the same file that was used to open the file } } public static bool ContainsAttribute(Collection<CustomAttribute> attrCollection, string attributeName) { foreach (var typeAttr in attrCollection) { if (typeAttr.AttributeType.FullName.Contains(attributeName)) return true; } return false; } private static void ModifyType(ModuleDefinition module, TypeDefinition type) { MethodReference classAttrCtor = module.ImportReference(typeof(MessagePackObjectAttribute).GetConstructor(new Type[] { typeof(bool) })); var typeAttr = new CustomAttribute(classAttrCtor); var boolTypeRef = module.ImportReference(typeof(bool)); var attrParamType = new CustomAttributeArgument(boolTypeRef, false); typeAttr.ConstructorArguments.Add(attrParamType); type.CustomAttributes.Add(typeAttr); int keyIndex = 0; foreach(var property in type.Properties) { if (ContainsAttribute(property.CustomAttributes, nameof(KeyAttribute))) continue; MethodReference fieldAttrCtor = module.ImportReference(typeof(KeyAttribute).GetConstructor(new Type[] { typeof(int) })); var attr = new CustomAttribute(fieldAttrCtor); var intTypeRef = module.ImportReference(typeof(int)); var attrParam = new CustomAttributeArgument(intTypeRef, keyIndex++); attr.ConstructorArguments.Add(attrParam); property.CustomAttributes.Add(attr); } } }