如何基于自定义特性从对象集合动态创建指定结构的对象?
嘿,这个需求我太熟了——基于自定义特性动态生成对象,刚好能解决你这种多字段、多特性关联的场景,我来给你捋个清晰的实现方案,完全适配你提到的20+字段、部分字段关联多种特性的实际情况。
核心思路
本质就是用反射扒出对象上带指定特性的属性,再通过**动态对象(ExpandoObject)**把这些属性映射成特性里定义的新名称,既灵活又能复用,不管你加多少特性都能轻松扩展。
1. 先把自定义特性的定义捋清楚(补全细节)
首先得确保特性类是反射能识别的,要继承Attribute,还要允许一个属性挂多个特性(AllowMultiple = true):
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class AccountAttribute : Attribute { public string Name { get; } public AccountAttribute(string name) => Name = name; } [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] public class RoleAttribute : Attribute { public string Name { get; } public RoleAttribute(string name) => Name = name; } // 你的Customer类(原样保留) public class Customer { [AccountAttribute(name: "CustomerAccountID")] public int CustomerID { get; set; } [RoleAttribute(name: "RoleUserID")] [AccountAttribute(name: "AccountRole")] public int RoleID { get; set; } }
2. 写个通用的转换方法(一次编写,终身复用)
这个方法不管你传哪种特性类型,都能自动筛选并生成动态对象:
using System.Dynamic; using System.Reflection; public static IEnumerable<ExpandoObject> ConvertToDynamicByAttribute<TAttribute>(IEnumerable<object> sourceList) where TAttribute : Attribute { var result = new List<ExpandoObject>(); var targetAttributeType = typeof(TAttribute); foreach (var sourceObj in sourceList) { dynamic dynamicObj = new ExpandoObject(); var objDictionary = (IDictionary<string, object>)dynamicObj; // 遍历源对象的所有公开属性 foreach (var prop in sourceObj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { // 检查当前属性是否包含目标特性 var attribute = prop.GetCustomAttribute(targetAttributeType) as TAttribute; if (attribute == null) continue; // 从特性中取出要映射的新属性名 var newPropName = attribute.GetType().GetProperty("Name")?.GetValue(attribute)?.ToString(); if (string.IsNullOrWhiteSpace(newPropName)) continue; // 将源属性的值映射到动态对象的新属性上 var propValue = prop.GetValue(sourceObj); objDictionary[newPropName] = propValue; } result.Add(dynamicObj); } return result; }
3. 调用示例(完全匹配你的需求)
直接用这个方法处理你的custData集合就行:
// 你的数据源 var custData = GetCustomerData(); // 👉 基于AccountAttribute筛选:保留CustomerID、RoleID,映射为CustomerAccountID、AccountRole var accountDynamicList = ConvertToDynamicByAttribute<AccountAttribute>(custData); // 👉 基于RoleAttribute筛选:仅保留RoleID,映射为RoleUserID var roleDynamicList = ConvertToDynamicByAttribute<RoleAttribute>(custData);
4. 大数据量场景优化建议
如果你的集合数据量很大,重复反射会影响性能,建议加个缓存,把每个类型的属性-特性映射关系存起来:
private static readonly ConcurrentDictionary<Type, Dictionary<PropertyInfo, string>> _attributeMappingCache = new(); // 在转换方法里先查缓存,没有再执行反射逻辑 var cacheKey = sourceObj.GetType(); if (!_attributeMappingCache.TryGetValue(cacheKey, out var propMapping)) { propMapping = new Dictionary<PropertyInfo, string>(); foreach (var prop in cacheKey.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var attribute = prop.GetCustomAttribute(targetAttributeType) as TAttribute; if (attribute == null) continue; var newPropName = attribute.GetType().GetProperty("Name")?.GetValue(attribute)?.ToString(); if (!string.IsNullOrWhiteSpace(newPropName)) { propMapping.Add(prop, newPropName); } } _attributeMappingCache.TryAdd(cacheKey, propMapping); } // 直接用缓存的映射构建动态对象 foreach (var (prop, newPropName) in propMapping) { objDictionary[newPropName] = prop.GetValue(sourceObj); }
测试验证效果
比如你有个测试Customer实例:
var testCust = new Customer { CustomerID = 1001, RoleID = 2002 }; var testList = new List<Customer> { testCust }; var accountResult = ConvertToDynamicByAttribute<AccountAttribute>(testList).First(); Console.WriteLine(accountResult.CustomerAccountID); // 输出1001 Console.WriteLine(accountResult.AccountRole); // 输出2002 var roleResult = ConvertToDynamicByAttribute<RoleAttribute>(testList).First(); Console.WriteLine(roleResult.RoleUserID); // 输出2002
完全符合你的要求吧!不管后面加多少字段、多少特性,这个方法都能直接复用。
内容的提问来源于stack exchange,提问作者Kurisinkal
相关产品推荐
相关产品推荐

