Cloning/Copying get accessor body to new type

十年热恋 提交于 2020-01-14 14:13:29

问题


I'm creating new type in dynamic assembly from existing type, but with only selected properties to include:

public class EmitTest
{
    public Type Create(Type prototype, Type dynamicBaseType, List<string> includedPropertyList)
    {
        AssemblyName aName = new AssemblyName("DynamicAssembly");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                aName,
                AssemblyBuilderAccess.RunAndSave);

        ModuleBuilder modulBuilder = assemblyBuilder.DefineDynamicModule(aName.Name, aName.Name + ".dll");


        string typeName = string.Concat(prototype.Name, "_DynamicType_", Guid.NewGuid().ToString().Replace("-", string.Empty));

        TypeBuilder typeBuilder = modulBuilder.DefineType(
            typeName,
            TypeAttributes.Public, null, new Type[] { });

        foreach (string s in includedPropertyList)
        {
            PropertyInfo propertyInfo = prototype.GetProperty(s);

            if (propertyInfo != null && dynamicBaseType.GetProperty(s) == null)
            {
                CreateProperty(typeBuilder, propertyInfo);
            }
        }

        return typeBuilder.CreateType();
    }

    #region Property Creation

    private void CreateProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo)
    {
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
            propertyInfo.Name,
            PropertyAttributes.HasDefault,
            propertyInfo.PropertyType,
            null);

        CreatePropertyBase(typeBuilder, propertyBuilder, propertyInfo);

        AddAttribute<BrowsableAttribute>(propertyBuilder, propertyInfo, CreatePropertyAttributeBrowsable);
        AddAttribute<DisplayNameAttribute>(propertyBuilder, propertyInfo, CreatePropertyAttributeDisplayName);          
    }

    private void CreatePropertyBase(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo propertyInfo)
    {

        FieldBuilder fieldBuilder = typeBuilder.DefineField(
            string.Concat("m_", propertyInfo.Name),
            propertyInfo.PropertyType,
            FieldAttributes.Private);

        MethodAttributes getSetAttr = MethodAttributes.Public |
            MethodAttributes.SpecialName | MethodAttributes.HideBySig;


        MethodBuilder mbGetAccessor = typeBuilder.DefineMethod(
            string.Concat("get_", propertyInfo.Name),
            getSetAttr,
            propertyInfo.PropertyType,
            Type.EmptyTypes);

        ILGenerator mbGetIL = mbGetAccessor.GetILGenerator();
        mbGetIL.Emit(OpCodes.Ldarg_0);
        mbGetIL.Emit(OpCodes.Ldfld, fieldBuilder);
        mbGetIL.Emit(OpCodes.Ret);


        MethodBuilder mbSetAccessor = typeBuilder.DefineMethod(
            string.Concat("set_", propertyInfo.Name),
            getSetAttr,
            null,
            new Type[] { propertyInfo.PropertyType });

        ILGenerator mbSetIL = mbSetAccessor.GetILGenerator();           
        mbSetIL.Emit(OpCodes.Ldarg_0);
        mbSetIL.Emit(OpCodes.Ldarg_1);
        mbSetIL.Emit(OpCodes.Stfld, fieldBuilder);
        mbSetIL.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(mbGetAccessor);
        propertyBuilder.SetSetMethod(mbSetAccessor);
    }

    #endregion Property Creation

    #region Attribute Createion

    private void AddAttribute<T>(PropertyBuilder propertyBuilder, PropertyInfo propertyInfo, Action<PropertyBuilder, T> action)
        where T : Attribute
    {
        T attribute = ReflectionHelper.GetAttribute(propertyInfo, typeof(T), false) as T;

        if (attribute != null)
        {
            action(propertyBuilder, attribute);
        }
    }

    private void CreatePropertyAttributeBrowsable(PropertyBuilder propertyBuilder, BrowsableAttribute browsableAttribute)
    {
        ConstructorInfo myAttrCtor = typeof(BrowsableAttribute).GetConstructor(new Type[] { typeof(bool) });
        CustomAttributeBuilder myAttr = new CustomAttributeBuilder(myAttrCtor, new object[] { browsableAttribute.Browsable });
        propertyBuilder.SetCustomAttribute(myAttr);
    }

    private void CreatePropertyAttributeDisplayName(PropertyBuilder propertyBuilder, DisplayNameAttribute displayNameAttribute)
    {
        ConstructorInfo myAttrCtor2 = typeof(DisplayNameAttribute).GetConstructor(new Type[] { typeof(string) });
        CustomAttributeBuilder myAttr2 = new CustomAttributeBuilder(myAttrCtor2, new object[] { displayNameAttribute.DisplayName });
        propertyBuilder.SetCustomAttribute(myAttr2);
    }

    #endregion Attribute Createion
}

This all works great, but here I'm creating only simple properties that just get or set private field.

I ran into cases where I have more complex property, for example in getter there is something like: "A + B * C" where A,B and C are properties in same class.

I tried creating copy of getter in CreatePropertyBase method like this:

        MethodBuilder mbNumberGetAccessor = typeBuilder.DefineMethod(
            string.Concat("get_", propertyInfo.Name),
            getSetAttr,
            propertyInfo.PropertyType,
            Type.EmptyTypes);       


        System.Reflection.MethodInfo mi = propertyInfo.GetGetMethod();
        byte[] body = mi.GetMethodBody().GetILAsByteArray();

        mbNumberGetAccessor.CreateMethodBody(body, body.Length);

This obviously didn't work. :)

My question is: Is it possible to copy body of get accessor that has references to other properties in same class, and if possible how to do it. I'm using .NET 3.5 SP1

Thanks.


回答1:


No, you'll have to dynamically decompile the IL from the source property because the IL in the source property would have static PropertyInfo references to the other properties it is referencing. And those references would be to the original PropertyInfo and not to your newly created ones, which is probably why it gave you errors.

Out of curiosity, what's the reason you don't just use an anonymous type to do the same thing?




回答2:


You can use this helper (CIL Reader) to traverse bodies of property accessors and reconstruct them with necessary modifications



来源:https://stackoverflow.com/questions/3372319/cloning-copying-get-accessor-body-to-new-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!