c# - Property Getter 的反射发射

标签 c# reflection reflection.emit typebuilder

我想像这样动态构建类型:

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);

你的 depth 属性类型错误,它必须是 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 });

关于c# - Property Getter 的反射发射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41393348/

相关文章:

c# - 尝试在 Winform 应用程序中显示大 (2 GB) Tiff 图像的一部分

c# - 在 ASP.NET Core 2 中获取 "SessionOptions.CookieName is obsolete"

c# - 如何使用 Entity Framework 6 自定义鉴别器列的名称、长度和值

c# - 如何发出 default(TimeSpan) 作为可选参数的默认值

java - 反射:抛出 java.lang.NullPointerException

reflection - 如何发出没有构造函数的新结构

c# - 在 MVC、C# 中的每个请求中运行一个方法?

c# - 在 C#/.NET 中模拟没有无参数构造函数的对象

c# - 为什么 IL.Emit 方法要添加额外的 nop 指令?

c# - 克隆/复制获取访问器主体到新类型