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

        }


    }
posted @ 2022-12-08 15:41  dewxin  阅读(94)  评论(0编辑  收藏  举报