C#.NET中如何将创建的简单匿名对象转换为自定义类类型
你之前尝试的括号强转、as运算符转换失败是必然结果:C# 中的匿名类型是编译器自动生成的独立密封类,和你自定义的SomeClass不存在任何继承、类型兼容关系,而这两种转换方式仅支持存在兼容关系的类型转换,和语法熟练度无关。
你找到的逐字段映射方案失效,基本都是因为实现逻辑只匹配了公共属性(Property),没有覆盖公共字段(Field)——你给出的SomeClass定义里,两个成员都是公共字段,不是带get/set访问器的属性,自然无法被常规映射逻辑识别赋值。
手动映射(性能最优,推荐字段少的场景用)
没有任何运行时开销,编译器会自动做类型检查,不会出现运行时匹配失败的问题:var test = new { FieldOne = "test2", FieldTwo = 2 }; SomeClass casted = new SomeClass { FieldOne = test.FieldOne, FieldTwo = test.FieldTwo };如果后续把
SomeClass的公共字段改成自动属性,写法还能更简洁。自定义通用映射方法(适配多场景)
如果项目里有大量类似转换需求,可以自己写个泛型扩展方法,同时匹配源和目标的公共字段、公共属性,按名称和类型兼容规则赋值即可,参考实现:public static class TypeConvertExtension { public static T CastTo<T>(this object source) where T : new() { if (source == null) throw new ArgumentNullException(nameof(source)); T instance = new T(); Type sourceType = source.GetType(); Type targetType = typeof(T); const BindingFlags flag = BindingFlags.Public | BindingFlags.Instance; // 匹配目标类公共字段 foreach (var tField in targetType.GetFields(flag)) { // 先找源对象的同名字段 var sField = sourceType.GetField(tField.Name, flag); if (sField != null && tField.FieldType.IsAssignableFrom(sField.FieldType)) { tField.SetValue(instance, sField.GetValue(source)); continue; } // 再找源对象的同名属性 var sProp = sourceType.GetProperty(tField.Name, flag); if (sProp != null && sProp.CanRead && tField.FieldType.IsAssignableFrom(sProp.PropertyType)) { tField.SetValue(instance, sProp.GetValue(source)); } } // 匹配目标类公共属性 foreach (var tProp in targetType.GetProperties(flag)) { if (!tProp.CanWrite) continue; // 先找源对象的同名属性 var sProp = sourceType.GetProperty(tProp.Name, flag); if (sProp != null && sProp.CanRead && tProp.PropertyType.IsAssignableFrom(sProp.PropertyType)) { tProp.SetValue(instance, sProp.GetValue(source)); continue; } // 再找源对象的同名字段 var sField = sourceType.GetField(tProp.Name, flag); if (sField != null && tProp.PropertyType.IsAssignableFrom(sField.FieldType)) { tProp.SetValue(instance, sField.GetValue(source)); } } return instance; } }调用方式非常简单:
var test = new { FieldOne = "test2", FieldTwo = 2 }; SomeClass casted = test.CastTo<SomeClass>();这个实现不管目标类用的是公共字段还是自动属性,只要成员名称、类型匹配就能正常赋值,不会出现你之前遇到的映射失效问题。
序列化中转(代码最简洁,适合已有序列化库依赖的场景)
如果项目里已经引入了JSON序列化库,可以先把匿名对象序列化为JSON,再反序列化为目标类型,不需要自己写映射逻辑,缺点是性能比前两种方案差,不适合高频调用的场景:// 以System.Text.Json为例 var test = new { FieldOne = "test2", FieldTwo = 2 }; SomeClass casted = JsonSerializer.Deserialize<SomeClass>(JsonSerializer.Serialize(test));
日常编写C#业务类时,优先使用公共自动属性代替公共字段,符合.NET编码规范,绝大多数官方类库、第三方工具默认都是针对属性做适配,能避免很多类似这次的适配问题,调整后的类定义如下:
public class SomeClass { public string FieldOne { get; set; } public int FieldTwo { get; set; } }
内容的提问来源于stack exchange,提问作者Froschkoenig84

