如何在C# System.Reflection.Emit中为类类型属性设置默认值
使用System.Reflection.Emit为动态类属性设置默认值
我正在用C#的System.Reflection.Emit在运行时创建动态类,想实现类似以下编译时类的效果:
public class RequestForTest { public string variableName { get; set; } public MyClass myClass { get; set; } = new MyClass(); }
也就是要给动态类的myClass属性设置默认值(实例化对应的类),该怎么实现?
更新内容
我定义了ClassProperty类:
public class ClassProperty { public string? Name { get; set; } public int Age { get; set; } = 5; }
我的动态类创建代码如下:
using System.Reflection.Emit; using System.Reflection; using SystemReflection; string className = "ReflectionDemo"; AssemblyName assemblyName = new AssemblyName(className); AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(className + "." + className + ".dll"); TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, null); // Add a private field of type int (Int32). FieldBuilder fieldBuilder = typeBuilder.DefineField( "_name", typeof(string), FieldAttributes.Private); // Next, we build the property. This involves building the property itself, as well as the // getter and setter methods. PropertyBuilder pbNumber = typeBuilder.DefineProperty( "Name", // Name PropertyAttributes.None, typeof(string), // Type of the property new Type[0]); // Types of indices, if any MethodBuilder mbSetNumber = typeBuilder.DefineMethod( "set_Name", // Name - setters are set_Property by convention // Setter is a special method and we don't want it to appear to callers from C# MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), // Setters don't return a value new[] { typeof(string) }); // We have a single argument of type System.Int32 // To generate the body of the method, we'll need an IL generator ILGenerator il = mbSetNumber.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // Load this il.Emit(OpCodes.Ldarg_1); // Load the new value il.Emit(OpCodes.Stfld, fieldBuilder); // Save the new value to this.m_number il.Emit(OpCodes.Ret); // Return // Finally, link the method to the setter of our property pbNumber.SetSetMethod(mbSetNumber); MethodBuilder mbGetNumber = typeBuilder.DefineMethod( "get_Name", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(string), new Type[0]); il = mbGetNumber.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // Load this il.Emit(OpCodes.Ldfld, fieldBuilder); // Load the value of this.m_number il.Emit(OpCodes.Ret); // Return the value pbNumber.SetGetMethod(mbGetNumber); ////////////////////////////////////////////////////////////////// // Create SystemReflection.ClassProperty FieldBuilder fieldBuilder1 = typeBuilder.DefineField( "_classProperty", typeof(ClassProperty), FieldAttributes.Private); // Next, we build the property. This involves building the property itself, as well as the // getter and setter methods. PropertyBuilder pbNumber1 = typeBuilder.DefineProperty( "ClassProperty", // Name PropertyAttributes.None, typeof(ClassProperty), // Type of the property new Type[0]); // Types of indices, if any MethodBuilder mbSetNumber1 = typeBuilder.DefineMethod( "set_ClassProperty", // Name - setters are set_Property by convention // Setter is a special method and we don't want it to appear to callers from C# MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), // Setters don't return a value new[] { typeof(ClassProperty) }); // We have a single argument of type System.Int32 // To generate the body of the method, we'll need an IL generator ILGenerator il1 = mbSetNumber1.GetILGenerator(); il1.Emit(OpCodes.Ldarg_0); // Load this il1.Emit(OpCodes.Ldarg_1); // Load the new value il1.Emit(OpCodes.Stfld, fieldBuilder); // Save the new value to this.m_number il1.Emit(OpCodes.Ret); // Return // Finally, link the method to the setter of our property pbNumber1.SetSetMethod(mbSetNumber1); MethodBuilder mbGetNumber1 = typeBuilder.DefineMethod( "get_ClassProperty", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(ClassProperty), new Type[0]); il1 = mbGetNumber1.GetILGenerator(); il1.Emit(OpCodes.Ldarg_0); // Load this il1.Emit(OpCodes.Ldfld, fieldBuilder); // Load the value of this.m_number il1.Emit(OpCodes.Ret); // Return the value pbNumber1.SetGetMethod(mbGetNumber1); ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, Type.EmptyTypes); //ConstructorInfo? constructorInfo = typeof(object).GetConstructor(Type.EmptyTypes); ConstructorInfo constructorInfo = typeBuilder.DefineDefaultConstructor(MethodAttributes.Public); ILGenerator constructorILGenerator = constructorBuilder.GetILGenerator(); if (constructorInfo == null) throw new Exception("Create constructor failed"); constructorILGenerator.Emit(OpCodes.Ldarg_0); constructorILGenerator.Emit(OpCodes.Call, constructorInfo); // Set Name constructorILGenerator.Emit(OpCodes.Ldarg_0); constructorILGenerator.Emit(OpCodes.Ldstr, "Anh"); constructorILGenerator.Emit(OpCodes.Stfld, fieldBuilder); // Set ClassProperty constructorILGenerator.Emit(OpCodes.Ldarg_0); ConstructorInfo? conInfo = typeof(ClassProperty).GetConstructor(new Type[0]); if (conInfo == null) throw new Exception($"Constructor on type {typeof(ClassProperty).Name} not found."); constructorILGenerator.Emit(OpCodes.Newobj, typeof(ClassProperty)); constructorILGenerator.Emit(OpCodes.Stfld, fieldBuilder1); constructorILGenerator.Emit(OpCodes.Nop); constructorILGenerator.Emit(OpCodes.Ret); var type = typeBuilder.CreateType(); if (type == null) throw new Exception($"Create type of {className} failed!"); var instanceType = Activator.CreateInstance(type); //instanceType.GetType().GetProperty("ClassProperty").SetValue(instanceType, Activator.CreateInstance(typeof(ClassProperty))); //Console.WriteLine("A"); Console.ReadKey();
问题修复与实现说明
你的代码存在两处关键错误,导致ClassProperty属性的默认值无法正确设置:
- 字段引用错误:在
set_ClassProperty和get_ClassProperty方法中,错误地引用了fieldBuilder(对应_name字段),而非fieldBuilder1(对应_classProperty字段)。 - 构造函数定义冲突:同时调用
DefineConstructor和DefineDefaultConstructor会导致构造函数重复定义,只需保留一种方式即可。
以下是修正后的完整代码:
using System.Reflection.Emit; using System.Reflection; public class ClassProperty { public string? Name { get; set; } public int Age { get; set; } = 5; } class Program { static void Main() { string className = "ReflectionDemo"; AssemblyName assemblyName = new AssemblyName(className); AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect); ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"{className}.dll"); TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, null); // 定义Name属性对应的私有字段 FieldBuilder nameField = typeBuilder.DefineField( "_name", typeof(string), FieldAttributes.Private); // 构建Name属性 PropertyBuilder nameProperty = typeBuilder.DefineProperty( "Name", PropertyAttributes.None, typeof(string), Type.EmptyTypes); // 构建Name的Setter MethodBuilder nameSetter = typeBuilder.DefineMethod( "set_Name", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), new[] { typeof(string) }); ILGenerator nameSetterIL = nameSetter.GetILGenerator(); nameSetterIL.Emit(OpCodes.Ldarg_0); nameSetterIL.Emit(OpCodes.Ldarg_1); nameSetterIL.Emit(OpCodes.Stfld, nameField); nameSetterIL.Emit(OpCodes.Ret); nameProperty.SetSetMethod(nameSetter); // 构建Name的Getter MethodBuilder nameGetter = typeBuilder.DefineMethod( "get_Name", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(string), Type.EmptyTypes); ILGenerator nameGetterIL = nameGetter.GetILGenerator(); nameGetterIL.Emit(OpCodes.Ldarg_0); nameGetterIL.Emit(OpCodes.Ldfld, nameField); nameGetterIL.Emit(OpCodes.Ret); nameProperty.SetGetMethod(nameGetter); // 定义ClassProperty属性对应的私有字段 FieldBuilder classPropertyField = typeBuilder.DefineField( "_classProperty", typeof(ClassProperty), FieldAttributes.Private); // 构建ClassProperty属性 PropertyBuilder classProperty = typeBuilder.DefineProperty( "ClassProperty", PropertyAttributes.None, typeof(ClassProperty), Type.EmptyTypes); // 构建ClassProperty的Setter MethodBuilder classPropertySetter = typeBuilder.DefineMethod( "set_ClassProperty", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), new[] { typeof(ClassProperty) }); ILGenerator cpsIL = classPropertySetter.GetILGenerator(); cpsIL.Emit(OpCodes.Ldarg_0); cpsIL.Emit(OpCodes.Ldarg_1); cpsIL.Emit(OpCodes.Stfld, classPropertyField); // 修正为正确的字段 cpsIL.Emit(OpCodes.Ret); classProperty.SetSetMethod(classPropertySetter); // 构建ClassProperty的Getter MethodBuilder classPropertyGetter = typeBuilder.DefineMethod( "get_ClassProperty", MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(ClassProperty), Type.EmptyTypes); ILGenerator cpgIL = classPropertyGetter.GetILGenerator(); cpgIL.Emit(OpCodes.Ldarg_0); cpgIL.Emit(OpCodes.Ldfld, classPropertyField); // 修正为正确的字段 cpgIL.Emit(OpCodes.Ret); classProperty.SetGetMethod(classPropertyGetter); // 自定义无参构造函数,实现字段初始化 ConstructorBuilder ctor = typeBuilder.DefineConstructor( MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, Type.EmptyTypes); ILGenerator ctorIL = ctor.GetILGenerator(); // 先调用基类(object)的构造函数 ConstructorInfo objectCtor = typeof(object).GetConstructor(Type.EmptyTypes)!; ctorIL.Emit(OpCodes.Ldarg_0); ctorIL.Emit(OpCodes.Call, objectCtor); // 初始化_name字段 ctorIL.Emit(OpCodes.Ldarg_0); ctorIL.Emit(OpCodes.Ldstr, "Anh"); ctorIL.Emit(OpCodes.Stfld, nameField); // 初始化_classProperty字段,实例化ClassProperty ConstructorInfo classPropertyCtor = typeof(ClassProperty).GetConstructor(Type.EmptyTypes)!; ctorIL.Emit(OpCodes.Ldarg_0); ctorIL.Emit(OpCodes.Newobj, classPropertyCtor); ctorIL.Emit(OpCodes.Stfld, classPropertyField); ctorIL.Emit(OpCodes.Ret); // 创建动态类型 Type dynamicType = typeBuilder.CreateType()!; // 实例化动态类 dynamic instance = Activator.CreateInstance(dynamicType)!; // 验证结果 Console.WriteLine($"Name: {instance.Name}"); Console.WriteLine($"ClassProperty.Age: {instance.ClassProperty.Age}"); // 输出5,说明ClassProperty已正确初始化 Console.ReadKey(); } }
关键实现要点
- 字段与属性的对应关系:确保每个属性的Getter/Setter都正确引用对应的私有字段,这是最容易出错的环节。
- 构造函数初始化逻辑:在自定义无参构造函数中,先调用基类构造函数,再通过
Newobj指令实例化ClassProperty,最后将实例赋值给对应私有字段,以此实现编译时= new MyClass()的默认值效果。 - 避免构造函数重复定义:不要同时调用
DefineConstructor和DefineDefaultConstructor,如需自定义初始化逻辑,使用DefineConstructor实现即可。
内容的提问来源于stack exchange,提问作者Đặng Đức
相关产品推荐
相关产品推荐

