C#无反射实现运行时获取类字段并接收用户输入初始化咨询
不使用反射实现C#动态UI表单生成与类初始化方案
核心实现思路
要避开运行时反射完成对新增子类的字段识别、表单生成、赋值初始化,可通过以下两种成熟方案实现,均完全符合需求:
方案1:编译时源生成器(推荐,适配后续无感知新增子类)
C# 提供的源生成器(Source Generator) 会在编译阶段扫描项目代码,自动生成额外的C#代码参与编译,全程无运行时反射开销,新增子类时不需要修改UI层代码。
实现步骤:
- 定义标记属性
[UIField],给所有需要UI识别的类成员添加该标记,可传入显示名、输入校验规则等扩展参数 - 实现自定义源生成器,扫描项目中所有继承自
Shape的子类,收集所有标记了[UIField]的成员的类型、名称、配置信息,编译时自动生成全局静态元数据字典,示例结构如下:
// 源生成器自动生成的代码 public static class ShapeMetadataStore { public static IReadOnlyDictionary<Type, List<FieldMeta>> Metadatas { get; } = new Dictionary<Type, List<FieldMeta>> { [typeof(Triangle)] = new List<FieldMeta> { new FieldMeta("angleType", typeof(Triangle.AngleType), "角度类型"), new FieldMeta("edgePropery", typeof(Triangle.EdgePropery), "边属性"), new FieldMeta("angle1", typeof(float), "角度1"), // 其余字段自动生成 }, // 其余Shape子类的元数据自动补全 }; // 同时自动生成每个子类的赋值扩展方法 public static void SetFields(this Triangle target, Dictionary<string, object> inputValues) { target.angleType = (Triangle.AngleType)inputValues[nameof(angleType)]; target.edgePropery = (Triangle.EdgePropery)inputValues[nameof(edgePropery)]; // 其余字段赋值逻辑自动生成 } }
- UI层直接从
ShapeMetadataStore.Metadatas中取出对应类型的字段元数据,根据字段类型生成对应输入控件:布尔值生成复选框、枚举类型生成下拉选择框、数值类型生成数字输入框 - 收集完用户输入后,调用自动生成的
SetFields扩展方法完成实例初始化
该方案下,后续新增Shape子类时,只需要给需要UI识别的字段加上[UIField]标记,编译时源生成器会自动更新元数据和赋值逻辑,UI层不需要做任何修改。
方案2:接口契约实现(无需额外学习成本,代码简单可控)
如果不想引入源生成器,可通过显式实现接口的方式完成需求,全程无反射:
- 定义元数据类与接口契约:
public class FieldMeta { public string FieldName { get; set; } public Type FieldType { get; set; } public string DisplayName { get; set; } public List<object> EnumOptions => FieldType.IsEnum ? Enum.GetValues(FieldType).Cast<object>().ToList() : null; } public interface IShapeFormAdaptor { List<FieldMeta> GetRequiredFields(); void FillFields(Dictionary<string, object> userInputs); }
- 所有
Shape子类实现IShapeFormAdaptor接口,示例:
public class Triangle : NonCircular, IShapeFormAdaptor { public enum AngleType { Right, Acute, Obtuse } public enum EdgePropery { Equilateral, Isosceles, Scalene } private AngleType angleType; private EdgePropery edgePropery; private float angle1, angle2, angle3; public List<FieldMeta> GetRequiredFields() { return new List<FieldMeta> { new() { FieldName = nameof(angleType), FieldType = typeof(AngleType), DisplayName = "角度类型" }, new() { FieldName = nameof(edgePropery), FieldType = typeof(EdgePropery), DisplayName = "边属性" }, new() { FieldName = nameof(angle1), FieldType = typeof(float), DisplayName = "角度1" }, new() { FieldName = nameof(angle2), FieldType = typeof(float), DisplayName = "角度2" }, new() { FieldName = nameof(angle3), FieldType = typeof(float), DisplayName = "角度3" } }; } public void FillFields(Dictionary<string, object> userInputs) { angleType = (AngleType)userInputs[nameof(angleType)]; edgePropery = (EdgePropery)userInputs[nameof(edgePropery)]; angle1 = (float)userInputs[nameof(angle1)]; angle2 = (float)userInputs[nameof(angle2)]; angle3 = (float)userInputs[nameof(angle3)]; } }
- UI层通用逻辑:
// 假设用户选择创建的Shape类型为targetType // 若需完全避开反射,可替换为手动实现的Shape工厂方法 var shapeInstance = (IShapeFormAdaptor)Activator.CreateInstance(targetType); var fields = shapeInstance.GetRequiredFields(); // 遍历fields生成对应输入控件,收集用户输入到userInputs字典 Dictionary<string, object> userInputs = GetInputFromUI(fields); // 完成赋值 shapeInstance.FillFields(userInputs); // 得到初始化完成的Shape实例 Shape finalShape = (Shape)shapeInstance;
小提示:如果要完全避开所有反射相关API,可替换
Activator.CreateInstance为手动实现的工厂方法,根据类型匹配返回对应子类实例即可,不影响核心逻辑。
内容的提问来源于stack exchange,提问作者Mosko
相关产品推荐
相关产品推荐

