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
相关产品推荐
相关产品推荐

