Я создаю новый тип в динамической сборке из существующего типа, но только с выбранными свойствами для включения:
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
}
Это все прекрасно работает, но здесь я создаю только простые свойства, которые просто получают или устанавливают приватное поле.
Я столкнулся со случаями, когда у меня есть более сложное свойство, например, в геттере есть что-то вроде: «A + B * C», где A, B и C - свойства в одном классе.
Я попытался создать копию метода получения в методе CreatePropertyBase следующим образом:
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);
Это явно не сработало. :)
Мой вопрос: возможно ли скопировать тело метода доступа get, который имеет ссылки на другие свойства в том же классе, и, если возможно, как это сделать.
Я использую .NET 3.5 SP1
Спасибо.