属性 Getter 的反射发射
Reflection Emit for Property Getter
我想像这样动态构建类型:
public class Sample
{
Sample Parent { get; set; }
public Sample(Sample parent)
{
Parent = parent;
}
public int Depth
{
get
{
if (Parent == null)
return -1;
else
return Parent.Depth + 1;
}
}
}
我写的代码是:
const string assemblyName = "SampleAssembly";
const string parentPproperty = "Parent";
const string depthProperty = "Depth";
const string typeName = "Sample";
const string assemblyFileName = assemblyName + ".dll";
AppDomain domain = AppDomain.CurrentDomain;
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
FieldBuilder parentField = typeBuilder.DefineField($"_{parentPproperty}", typeBuilder, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(parentPproperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes);
MethodAttributes getSetAttr = MethodAttributes.Public |
MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder getParentMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, parentField.FieldType, Type.EmptyTypes);
ILGenerator il = getParentMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, parentField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getParentMethod);
MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", qetSetAttr, null, Type.EmptyTypes);
il = setParentMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, parentField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setParentMethod);
parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private);
propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes);
MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr , parentField.FieldType, Type.EmptyTypes);
il = getDepthMethod.GetILGenerator();
LocalBuilder lb = il.DeclareLocal(typeof(bool));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Brfalse_S);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br_S);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Callvirt, getDepthMethod);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br_S);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getDepthMethod);
ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeBuilder });
il= constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, setParentMethod);
il.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
var obj1 = Activator.CreateInstance(type, null);
var obj2 = Activator.CreateInstance(type, obj1);
assemblyBuilder.Save(assemblyFileName);
我想我在构建构造函数和 Depth 属性 getter 方法时遇到了问题。
请帮我摆脱困境。
实例也未创建。
谢谢
您的代码中存在一些问题。
在您的父 setter 方法中,您错过了声明参数:
MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new [] { propertyBuilder.PropertyType });
您在这里声明了一个冗余的支持字段,只需删除此行:
parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private);
你的深度 属性 类型错误,必须是 int 类型:
propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, typeof(int), Type.EmptyTypes);
MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes);
您为计算 属性 生成的 IL 代码看起来像是调试代码,我用发布代码替换了它。此外,您正在发出不完整的分支指令,您应该将目标标签作为第二个参数传递,看看这个工作方法主体:
il = getDepthMethod.GetILGenerator();
var notNullLabel = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Brtrue_S, notNullLabel);
il.Emit(OpCodes.Ldc_I4_M1);
il.Emit(OpCodes.Ret);
il.MarkLabel(notNullLabel);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Callvirt, getDepthMethod);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
最后但同样重要的是,您必须将 null 参数包装在一个对象数组中,以便激活器可以找到预期的构造函数重载:
var obj1 = Activator.CreateInstance(type, new object[] { null });
我想像这样动态构建类型:
public class Sample
{
Sample Parent { get; set; }
public Sample(Sample parent)
{
Parent = parent;
}
public int Depth
{
get
{
if (Parent == null)
return -1;
else
return Parent.Depth + 1;
}
}
}
我写的代码是:
const string assemblyName = "SampleAssembly";
const string parentPproperty = "Parent";
const string depthProperty = "Depth";
const string typeName = "Sample";
const string assemblyFileName = assemblyName + ".dll";
AppDomain domain = AppDomain.CurrentDomain;
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
FieldBuilder parentField = typeBuilder.DefineField($"_{parentPproperty}", typeBuilder, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(parentPproperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes);
MethodAttributes getSetAttr = MethodAttributes.Public |
MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder getParentMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, parentField.FieldType, Type.EmptyTypes);
ILGenerator il = getParentMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, parentField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getParentMethod);
MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", qetSetAttr, null, Type.EmptyTypes);
il = setParentMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, parentField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setParentMethod);
parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private);
propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, parentField.FieldType, Type.EmptyTypes);
MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr , parentField.FieldType, Type.EmptyTypes);
il = getDepthMethod.GetILGenerator();
LocalBuilder lb = il.DeclareLocal(typeof(bool));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Ldnull);
il.Emit(OpCodes.Ceq);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Brfalse_S);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br_S);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Callvirt, getDepthMethod);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br_S);
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getDepthMethod);
ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeBuilder });
il= constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, setParentMethod);
il.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
var obj1 = Activator.CreateInstance(type, null);
var obj2 = Activator.CreateInstance(type, obj1);
assemblyBuilder.Save(assemblyFileName);
我想我在构建构造函数和 Depth 属性 getter 方法时遇到了问题。
请帮我摆脱困境。
实例也未创建。
谢谢
您的代码中存在一些问题。
在您的父 setter 方法中,您错过了声明参数:
MethodBuilder setParentMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new [] { propertyBuilder.PropertyType });
您在这里声明了一个冗余的支持字段,只需删除此行:
parentField = typeBuilder.DefineField($"_{depthProperty}", typeBuilder, FieldAttributes.Private);
你的深度 属性 类型错误,必须是 int 类型:
propertyBuilder = typeBuilder.DefineProperty(depthProperty, PropertyAttributes.None, typeof(int), Type.EmptyTypes);
MethodBuilder getDepthMethod = typeBuilder.DefineMethod($"get_{depthProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes);
您为计算 属性 生成的 IL 代码看起来像是调试代码,我用发布代码替换了它。此外,您正在发出不完整的分支指令,您应该将目标标签作为第二个参数传递,看看这个工作方法主体:
il = getDepthMethod.GetILGenerator();
var notNullLabel = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Brtrue_S, notNullLabel);
il.Emit(OpCodes.Ldc_I4_M1);
il.Emit(OpCodes.Ret);
il.MarkLabel(notNullLabel);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getParentMethod);
il.Emit(OpCodes.Callvirt, getDepthMethod);
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
最后但同样重要的是,您必须将 null 参数包装在一个对象数组中,以便激活器可以找到预期的构造函数重载:
var obj1 = Activator.CreateInstance(type, new object[] { null });