C# 9 Record的ToString()输出如何反向实例化对应Record类型对象
错误原因说明
Convert.ChangeType 抛出异常的原因是它仅支持实现了IConvertible接口的类型之间的内置转换,自定义record类型没有定义从string到自身的转换规则,也没有实现IConvertible接口,因此无法直接转换。
方案1:无需第三方序列化器的通用Record字符串解析实现
该方案直接解析record默认ToString输出的固定格式,仅对record类型生效,适合简单基础类型属性的record场景:
实现代码
using System.ComponentModel; using System.Reflection; public static class RecordParserExtensions { // 判断是否为record类型 private static bool IsRecordType(Type type) { return type.GetMethod("<Clone>$", BindingFlags.Instance | BindingFlags.Public) != null; } public static T FromRecordString<T>(this string recordString) where T : class { return (T)FromRecordString(recordString, typeof(T)); } public static object FromRecordString(this string recordString, Type targetType) { // 校验目标类型是record if (!IsRecordType(targetType)) throw new ArgumentException("目标类型必须是record类型", nameof(targetType)); // 解析固定格式:TypeName { Prop1 = val1, Prop2 = val2 } var span = recordString.AsSpan().Trim(); var braceStart = span.IndexOf('{'); var braceEnd = span.LastIndexOf('}'); if (braceStart == -1 || braceEnd == -1) throw new FormatException("字符串不符合Record默认ToString格式"); // 提取属性部分 var propsPart = span.Slice(braceStart + 1, braceEnd - braceStart - 1).Trim(); var propPairs = propsPart.ToString().Split(',', StringSplitOptions.RemoveEmptyEntries); // 获取record主构造函数(默认record的主构造函数参数和属性一一对应) var ctor = targetType.GetConstructors().OrderBy(c => c.GetParameters().Length).First(); var ctorParams = ctor.GetParameters(); var args = new object[ctorParams.Length]; foreach (var pair in propPairs) { var kv = pair.Split('=', 2); if (kv.Length != 2) continue; var propName = kv[0].Trim(); var propValue = kv[1].Trim(); // 匹配构造函数参数 var param = ctorParams.FirstOrDefault(p => p.Name.Equals(propName, StringComparison.OrdinalIgnoreCase)); if (param == null) continue; // 转换值类型 var converter = TypeDescriptor.GetConverter(param.ParameterType); args[Array.IndexOf(ctorParams, param)] = converter.ConvertFromString(propValue)!; } return ctor.Invoke(args); } }
使用示例
var recordString = "SimpleRec { PropertyName = property }"; var rec = recordString.FromRecordString<SimpleRec>(); // rec.PropertyName 等于 "property"
方案局限
- 仅支持属性为基础类型(string、int、DateTime等内置类型)的record
- 无法处理属性值包含逗号、大括号的场景(比如string属性值本身含有
}的情况会解析失败) - 不支持嵌套record、集合属性等复杂结构
- 依赖编译器生成的record默认ToString格式,微软未承诺该格式永久不变,存在版本兼容风险
方案2:生产环境推荐实现
默认ToString的设计定位是调试输出,而非序列化载体,因此更推荐放弃使用默认ToString做转换,选择以下更可靠的方案:
方案2.1 自定义序列化逻辑(无序列化器依赖)
针对单个record类型重写ToString并添加静态Parse方法,性能最高,无额外依赖:
public record SimpleRec(string PropertyName) { public override string ToString() => PropertyName; public static SimpleRec Parse(string input) => new(input.Trim()); } // 使用方式 var str = new SimpleRec("property").ToString(); var rec = SimpleRec.Parse(str);
方案2.2 内置JSON序列化(通用场景首选)
.NET自带的System.Text.Json不需要引入第三方依赖,稳定性、兼容性远高于自定义解析逻辑,适合需要通用处理多个record类型的场景:
// 序列化 var simpleRecord = new SimpleRec("property"); var json = System.Text.Json.JsonSerializer.Serialize(simpleRecord); // 反序列化 var rec = System.Text.Json.JsonSerializer.Deserialize<SimpleRec>(json);
内容的提问来源于stack exchange,提问作者jwdonahue
相关产品推荐
相关产品推荐

