You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于自定义特性从对象集合动态创建指定结构的对象?

嘿,这个需求我太熟了——基于自定义特性动态生成对象,刚好能解决你这种多字段、多特性关联的场景,我来给你捋个清晰的实现方案,完全适配你提到的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:43:02