C#中如何实现对象到匿名类型的转换?求完善ToCast方法
匿名类型与动态对象的自动转换实现
完整扩展方法实现
替换你原代码中ToCast方法的实现,以下是完整的扩展类:
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Dynamic; using System.Linq; using System.Reflection; public static class Extensions { public static IEnumerable<T> ToEnumerable<T>(this T value) { yield return value; } public static dynamic ToDynamic(this object value) { IDictionary<string, object> e = new ExpandoObject(); foreach (PropertyDescriptor item in TypeDescriptor.GetProperties(value.GetType())) { e.Add(item.Name, item.GetValue(value)); } return e; } public static IEnumerable<T> ToCast<T>(this IEnumerable<object> value, T sample) where T : class { if (value == null) yield break; var targetType = typeof(T); foreach (var item in value) { yield return item.CastToType(sample); } } public static T ToCastFirstorDefault<T>(this object value, T sample) where T : class { return value.ToEnumerable().ToCast(sample).Take(1).FirstOrDefault(); } // 核心转换逻辑:将object转换为指定匿名类型 private static T CastToType<T>(this object source, T sample) where T : class { if (source == null) return default; var sourceType = source.GetType(); var targetType = typeof(T); var targetCtor = targetType.GetConstructors().First(); var targetParams = targetCtor.GetParameters(); // 匹配属性并转换值 var paramValues = targetParams.Select(param => { var sourceProp = sourceType.GetProperty(param.Name); if (sourceProp == null) return param.DefaultValue; var sourceValue = sourceProp.GetValue(source); // 处理集合类型 if (param.ParameterType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(param.ParameterType)) { var elementType = param.ParameterType.GetGenericArguments()[0]; // 如果是匿名类型,用示例对象的属性获取元素类型样本 if (elementType.IsClass && elementType.IsDefined(typeof(CompilerGeneratedAttribute), false)) { var sampleElement = ((IEnumerable)sample.GetType().GetProperty(param.Name).GetValue(sample))?.Cast<object>().FirstOrDefault(); if (sampleElement != null) { var castMethod = typeof(Extensions).GetMethod(nameof(ToCast)).MakeGenericMethod(elementType); return castMethod.Invoke(null, new object[] { ((IEnumerable)sourceValue).Cast<object>(), sampleElement }); } } // 非匿名集合直接转换 return ((IEnumerable)sourceValue).Cast(elementType); } // 普通类型直接转换 return Convert.ChangeType(sourceValue, param.ParameterType); }).ToArray(); return (T)targetCtor.Invoke(paramValues); } }
调用示例
按照你期望的方式使用即可:
// 直接转换顶层对象 var rslt = Model.ToCastFirstorDefault(new { take = 0, data = default(object[]) }); // 嵌套转换集合内的匿名类型 var rsltWithNested = Model.ToCastFirstorDefault(new { take = 0, data = new[] { new { tarih = default(DateTime), proje = "", durum = false } } }).ToEnumerable().Select(x => new { x.take, data = x.data.ToCast(new { tarih = default(DateTime), proje = "", durum = false }) }).Take(1).FirstOrDefault();
实现说明
- 核心原理:利用反射获取匿名类型的构造函数与参数信息,匹配源对象的同名属性,递归处理嵌套集合和匿名类型元素。
- 集合处理:判断目标类型是否为可枚举类型,如果元素是匿名类型,则通过样本对象获取元素类型,调用泛型
ToCast方法完成批量转换。 - 为什么不用Convert.ChangeType:匿名类型是编译器自动生成的密封类型,
Convert.ChangeType无法处理复杂嵌套结构和匿名类型的实例化,而反射可以直接调用其构造函数,逐个属性赋值转换。
内容的提问来源于stack exchange,提问作者Uğur Demirel
相关产品推荐
相关产品推荐

