如何以单个CSV字符串为输入创建指定字段的C#对象?
这问题我熟,刚好有几种实用的方案,看你具体需求选就行:
方案1:用ExpandoObject快速实现动态对象
如果不需要强类型,只是临时用用,这个方案最省心——直接用ExpandoObject生成动态属性,默认值全部设为空字符串。代码写起来超简单:
using System.Dynamic; using System.Linq; public static dynamic CreateDynamicObject(string fieldsCsv) { dynamic obj = new ExpandoObject(); // 拆分CSV并去掉每个字段的前后空格 var fields = fieldsCsv.Split(',').Select(field => field.Trim()); foreach (var field in fields) { // 把每个字段作为属性添加到动态对象,默认值为空字符串 ((IDictionary<string, object>)obj)[field] = string.Empty; } return obj; } // 使用示例 string fields = "Id,Name,Code"; dynamic myObj = CreateDynamicObject(fields); Console.WriteLine(myObj.Id); // 输出空字符串 myObj.Name = "Alice"; // 直接赋值就行,跟普通对象一样用
优点是快、代码少,适合原型开发或者不需要编译时属性检查的场景;缺点是没有强类型支持,拼写错属性名只会在运行时报错。
方案2:运行时生成强类型对象(反射Emit)
如果需要强类型(比如希望编译时能检查属性名,或者要跟强类型API交互),可以用反射在运行时动态生成类,然后创建实例。每个字段都会生成对应的string属性,构造函数里自动把所有属性初始化为空字符串:
using System.Reflection; using System.Reflection.Emit; public static object CreateStrongTypeObject(string fieldsCsv) { var fields = fieldsCsv.Split(',').Select(f => f.Trim()).ToArray(); // 1. 创建动态程序集和模块 var assemblyName = new AssemblyName("DynamicGeneratedTypes"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); // 2. 定义动态类 var typeBuilder = moduleBuilder.DefineType( "DynamicDataObject", TypeAttributes.Public | TypeAttributes.Class); // 3. 为每个字段生成属性和私有字段 foreach (var field in fields) { // 生成私有字段 var privateField = typeBuilder.DefineField($"_{field}", typeof(string), FieldAttributes.Private); // 生成公共属性 var property = typeBuilder.DefineProperty( field, PropertyAttributes.None, typeof(string), null); // 生成get方法 var getMethod = typeBuilder.DefineMethod( $"get_{field}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), Type.EmptyTypes); var getIl = getMethod.GetILGenerator(); getIl.Emit(OpCodes.Ldarg_0); getIl.Emit(OpCodes.Ldfld, privateField); getIl.Emit(OpCodes.Ret); // 生成set方法 var setMethod = typeBuilder.DefineMethod( $"set_{field}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new[] { typeof(string) }); var setIl = setMethod.GetILGenerator(); setIl.Emit(OpCodes.Ldarg_0); setIl.Emit(OpCodes.Ldarg_1); setIl.Emit(OpCodes.Stfld, privateField); setIl.Emit(OpCodes.Ret); // 绑定属性的get/set方法 property.SetGetMethod(getMethod); property.SetSetMethod(setMethod); } // 4. 生成构造函数,初始化所有字段为空字符串 var ctorBuilder = typeBuilder.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); var ctorIl = ctorBuilder.GetILGenerator(); foreach (var field in fields) { var privateField = typeBuilder.GetField($"_{field}", BindingFlags.NonPublic | BindingFlags.Instance); ctorIl.Emit(OpCodes.Ldarg_0); ctorIl.Emit(OpCodes.Ldstr, string.Empty); ctorIl.Emit(OpCodes.Stfld, privateField); } ctorIl.Emit(OpCodes.Ret); // 5. 完成类型创建并实例化 var dynamicType = typeBuilder.CreateType(); return Activator.CreateInstance(dynamicType); } // 使用示例 string fields = "Id,Name,Code"; var myObj = CreateStrongTypeObject(fields); // 可以用反射访问属性,或者用dynamic包装后直接调用 dynamic dynamicWrapper = myObj; Console.WriteLine(dynamicWrapper.Code); // 输出空字符串 dynamicWrapper.Id = "1001";
这个方案生成的是真正的强类型对象,适合需要类型安全但字段只能在运行时确定的场景。如果经常用同一份字段,可以缓存生成的类型,避免重复生成的性能开销。
方案3:编译时源代码生成(性能最优)
如果你的CSV字段在编译时就能确定(比如是固定的配置字符串),那用C#源代码生成器是最优解——编译时直接生成对应的类,跟手写的类完全一样,性能拉满,还能享受强类型的所有好处。
生成的类大概长这样:
public class GeneratedDataObject { public string Id { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; public string Code { get; set; } = string.Empty; }
实现思路是创建一个ISourceGenerator,在编译阶段读取你的CSV字段字符串,自动生成类的源代码并添加到编译上下文。这个方案适合长期使用、字段相对固定的场景,完全没有运行时额外开销。
内容的提问来源于stack exchange,提问作者CodeBoldedDeveloper
相关产品推荐
相关产品推荐

