views:

84

answers:

2

Hi,

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.

A: 

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?

Kirk Woll
I have to work with literals, array of strings that contain property names, and I have to add attributes to those properties etc. I can't see how can I use anonymous types with all that, or can I? :)
Andrija
A: 

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

desco