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