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.