使用反射为动态创建的属性赋值时遇到的问题
问题分析与解决方案
你的代码核心问题在于:为同一个属性的get和set方法分别定义了两个独立的后备字段,导致赋值操作(set)和取值操作(get)完全在不同的存储位置进行,自然看不到赋值后的结果。此外,你提到“不需要后备字段”是误解——普通的可读写属性必须依赖某个存储载体来保存值,C#的自动属性也是编译器自动生成了后备字段,动态创建属性时需要显式定义这个共享的存储字段。
修正后的代码
替换原代码中的foreach循环部分:
foreach (var category in model.Categories) { var propertyName = category.ColumnName.Replace(" ", "_"); var newPropType = typeof(string); // 1. 定义该属性唯一的私有后备字段 FieldBuilder backingField = typeBuilder.DefineField($"_{propertyName}", newPropType, FieldAttributes.Private); // 2. 定义属性本身 PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, newPropType, null); // 3. 实现get方法:从共享字段读取值 MethodBuilder getMethod = typeBuilder.DefineMethod($"get_{propertyName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, newPropType, Type.EmptyTypes); ILGenerator getIL = getMethod.GetILGenerator(); getIL.Emit(OpCodes.Ldarg_0); getIL.Emit(OpCodes.Ldfld, backingField); getIL.Emit(OpCodes.Ret); // 4. 实现set方法:将值写入共享字段 MethodBuilder setMethod = typeBuilder.DefineMethod($"set_{propertyName}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[] { newPropType }); ILGenerator setIL = setMethod.GetILGenerator(); setIL.Emit(OpCodes.Ldarg_0); setIL.Emit(OpCodes.Ldarg_1); setIL.Emit(OpCodes.Stfld, backingField); setIL.Emit(OpCodes.Ret); // 5. 将get/set方法关联到属性 propertyBuilder.SetGetMethod(getMethod); propertyBuilder.SetSetMethod(setMethod); }
关键修正点
- 每个属性只创建一个私有后备字段,让get和set方法都操作这个字段,保证赋值和取值的存储位置一致。
- 先创建后备字段,再在get/set的IL指令中引用该字段,避免重复创建字段(原代码中每次调用
DefineField都会生成新字段,即使名称重复也会因为调用时机问题导致异常)。
内容的提问来源于stack exchange,提问作者CrsB1
相关产品推荐
相关产品推荐

