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

TypeScript:保留泛型参数时DTO转类的构造函数类型问题

如何在Template类构造函数中定义对应泛型PropertyDto的参数类型

核心思路

要处理不同泛型参数的PropertyDto<T>数组并映射为对应Property<T>实例,关键是通过非泛型基接口/基类统一不同泛型的DTO类型,同时在构造函数中利用反射或映射库完成类型匹配与实例化。

具体实现步骤

1. 定义非泛型基接口统一DTO类型

先创建一个非泛型接口,让所有PropertyDto<T>实现它,这样就能用该接口类型的集合来容纳不同泛型的DTO实例:

// 非泛型基接口,统一所有PropertyDto<T>
public interface IPropertyDto
{
    string Name { get; }
    object ValueAsObject { get; }
}

// 泛型PropertyDto实现基接口
public class PropertyDto<T> : IPropertyDto
{
    public string Name { get; set; }
    public T Value { get; set; }

    // 实现非泛型属性,用于统一访问值
    public object ValueAsObject => Value;
}

2. 定义对应的泛型Property类及非泛基接口(可选)

为了统一后续实例的存储,也可以给Property<T>定义非泛基接口:

public interface IProperty
{
    string Name { get; }
    object ValueAsObject { get; }
}

public class Property<T> : IProperty
{
    public string Name { get; set; }
    public T Value { get; set; }

    // 从PropertyDto<T>直接构造
    public Property(PropertyDto<T> dto)
    {
        Name = dto.Name;
        Value = dto.Value;
    }

    public object ValueAsObject => Value;
}

3. Template类构造函数的参数类型与映射逻辑

Template类的构造函数参数应使用IEnumerable<IPropertyDto>(或IPropertyDto[]),这样可以接收所有不同泛型的PropertyDto<T>实例。内部通过反射匹配TProperties中的属性,完成映射:

public class Template<TProperties> where TProperties : new()
{
    public TProperties Properties { get; }

    // 构造函数参数:包含所有对应泛型的PropertyDto的集合
    public Template(IEnumerable<IPropertyDto> propertyDtos)
    {
        Properties = new TProperties();
        var propertyInfos = typeof(TProperties).GetProperties();

        foreach (var propInfo in propertyInfos)
        {
            // 获取当前Property<T>的泛型参数类型
            var propertyGenericType = propInfo.PropertyType.GetGenericArguments()[0];
            
            // 匹配同名称、同值类型的PropertyDto
            var matchingDto = propertyDtos.FirstOrDefault(dto => 
                dto.Name == propInfo.Name && 
                (propertyGenericType.IsValueType ? 
                    dto.ValueAsObject.GetType() == propertyGenericType : 
                    propertyGenericType.IsInstanceOfType(dto.ValueAsObject)));

            if (matchingDto == null) continue;

            // 动态创建Property<T>实例并赋值
            var propertyType = typeof(Property<>).MakeGenericType(propertyGenericType);
            var dtoType = typeof(PropertyDto<>).MakeGenericType(propertyGenericType);
            var constructor = propertyType.GetConstructor(new[] { dtoType });
            var typedDto = Convert.ChangeType(matchingDto, dtoType);
            var propertyInstance = constructor.Invoke(new[] { typedDto });

            propInfo.SetValue(Properties, propertyInstance);
        }
    }
}

4. 简化映射(使用AutoMapper)

如果不想手动写反射逻辑,可以用AutoMapper来处理泛型类型的映射:

// 提前配置AutoMapper映射规则
var mapperConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap(typeof(PropertyDto<>), typeof(Property<>));
});
var mapper = mapperConfig.CreateMapper();

// 修改Template构造函数
public Template(IEnumerable<IPropertyDto> propertyDtos)
{
    Properties = new TProperties();
    var propertyInfos = typeof(TProperties).GetProperties();

    foreach (var propInfo in propertyInfos)
    {
        var propertyGenericType = propInfo.PropertyType.GetGenericArguments()[0];
        var matchingDto = propertyDtos.FirstOrDefault(dto => 
            dto.Name == propInfo.Name && 
            (propertyGenericType.IsValueType ? 
                dto.ValueAsObject.GetType() == propertyGenericType : 
                propertyGenericType.IsInstanceOfType(dto.ValueAsObject)));

        if (matchingDto == null) continue;

        // 用AutoMapper完成泛型映射
        var dtoType = typeof(PropertyDto<>).MakeGenericType(propertyGenericType);
        var propertyInstance = mapper.Map(matchingDto, dtoType, propInfo.PropertyType);
        propInfo.SetValue(Properties, propertyInstance);
    }
}

示例用法

假设我们有如下TProperties类型:

public class UserTemplateProperties
{
    public Property<string> Username { get; set; }
    public Property<int> Age { get; set; }
    public Property<DateTime> RegisterDate { get; set; }
}

使用时:

// 模拟API返回的DTO数组
var dtos = new IPropertyDto[]
{
    new PropertyDto<string> { Name = "Username", Value = "john_doe" },
    new PropertyDto<int> { Name = "Age", Value = 30 },
    new PropertyDto<DateTime> { Name = "RegisterDate", Value = new DateTime(2023, 1, 1) }
};

// 创建Template实例
var userTemplate = new Template<UserTemplateProperties>(dtos);

// 访问映射后的属性
Console.WriteLine(userTemplate.Properties.Username.Value); // 输出 john_doe
Console.WriteLine(userTemplate.Properties.Age.Value); // 输出 30

内容的提问来源于stack exchange,提问作者wertzui

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 08:52:49