1. 程式人生 > 其它 >使用Mono.Cecil給訊息類自動新增Attribute

使用Mono.Cecil給訊息類自動新增Attribute

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);
            }

        }


    }