C#运行时为属性分配类型:动态指定ChildAttribute的childNodeValue类型
首先得纠正你代码里的一个小问题:C# 里并没有 Type<T> 这种写法,这会直接导致编译错误。要实现childNodeValue支持string或List<ChildAttribute>两种类型,我们有几种不同的实现思路,下面给你详细拆解:
方案1:使用
object类型(最简单直接) 这是最快速的实现方式,把childNodeValue的类型定义为object——因为object是所有类型的基类,自然能容纳string和List<ChildAttribute>。
修正后的类定义
public class ChildAttribute { public int? sequence { get; set; } public object childNodeValue { get; set; } // 可容纳string或List<ChildAttribute> public int? parentId { get; set; } }
循环中的类型分配
假设你的pNodes集合里的元素有明确标识(比如IsLeaf属性)来区分是叶子节点(对应string)还是父节点(对应子列表),循环代码可以这么写:
foreach (var pItem in pNodes) { var childAttr = new ChildAttribute(); // 根据节点类型分配对应的值 if (pItem.IsLeaf) { // 叶子节点:赋值string类型 childAttr.childNodeValue = pItem.TextContent; } else { // 父节点:递归创建子ChildAttribute列表 var childList = new List<ChildAttribute>(); foreach (var childNode in pItem.ChildNodes) { // 复用当前逻辑递归处理子节点 var subChild = new ChildAttribute(); subChild.sequence = childNode.Sequence; subChild.parentId = pItem.Id; // 同样判断子节点类型赋值childNodeValue... childList.Add(subChild); } childAttr.childNodeValue = childList; } // 赋值其他属性 childAttr.sequence = pItem.Sequence; childAttr.parentId = pItem.ParentId; }
注意:这种方式的缺点是类型不安全——你需要在使用childNodeValue的时候手动判断类型(比如用is关键字或as转换),否则容易抛出类型转换异常。
方案2:泛型类实现类型安全
如果想要严格的类型检查,可以把ChildAttribute定义为泛型类,这样childNodeValue的类型在编译时就是明确的。
泛型类定义
// 先定义非泛型基类,方便统一遍历 public abstract class ChildAttributeBase { public int? sequence { get; set; } public int? parentId { get; set; } } public class ChildAttribute<T> : ChildAttributeBase { public T childNodeValue { get; set; } }
循环中的使用
foreach (var pItem in pNodes) { ChildAttributeBase childAttr; if (pItem.IsLeaf) { // 创建string类型的子属性 childAttr = new ChildAttribute<string> { childNodeValue = pItem.TextContent, sequence = pItem.Sequence, parentId = pItem.ParentId }; } else { // 创建List<ChildAttributeBase>类型的子属性 var childList = new List<ChildAttributeBase>(); // 递归处理子节点... childAttr = new ChildAttribute<List<ChildAttributeBase>> { childNodeValue = childList, sequence = pItem.Sequence, parentId = pItem.ParentId }; } // 将childAttr添加到目标集合中 }
这种方式的优点是编译时就能保证类型正确,不会出现意外的类型转换错误,但缺点是需要额外处理泛型和基类的关系,遍历的时候只能通过基类操作。
方案3:自定义Discriminated Union(类型安全且优雅)
如果你的项目使用C# 8.0及以上版本,可以实现一个简单的区分联合类型,既保证类型安全,又能优雅地处理两种类型的赋值和读取。
自定义联合类型
public class ChildNodeValue { private readonly object _value; // 私有构造函数,只能通过静态方法创建实例 private ChildNodeValue(object value) => _value = value; // 静态工厂方法:创建string类型的实例 public static ChildNodeValue FromString(string value) => new ChildNodeValue(value); // 静态工厂方法:创建List<ChildAttribute>类型的实例 public static ChildNodeValue FromList(List<ChildAttribute> value) => new ChildNodeValue(value); // 匹配方法:优雅处理两种类型的逻辑 public TResult Match<TResult>(Func<string, TResult> stringHandler, Func<List<ChildAttribute>, TResult> listHandler) { return _value switch { string s => stringHandler(s), List<ChildAttribute> list => listHandler(list), _ => throw new InvalidOperationException("不支持的类型") }; } } // 修正后的ChildAttribute类 public class ChildAttribute { public int? sequence { get; set; } public ChildNodeValue childNodeValue { get; set; } public int? parentId { get; set; } }
循环中的赋值
foreach (var pItem in pNodes) { var childAttr = new ChildAttribute(); if (pItem.IsLeaf) { childAttr.childNodeValue = ChildNodeValue.FromString(pItem.TextContent); } else { var childList = new List<ChildAttribute>(); // 递归处理子节点... childAttr.childNodeValue = ChildNodeValue.FromList(childList); } childAttr.sequence = pItem.Sequence; childAttr.parentId = pItem.ParentId; }
使用时的类型处理
当你需要读取childNodeValue时,可以通过Match方法优雅地处理两种类型:
childAttr.childNodeValue.Match( str => Console.WriteLine($"字符串值:{str}"), list => Console.WriteLine($"子节点数量:{list.Count}") );
这种方式兼顾了类型安全和代码优雅,是比较推荐的现代C#实现方式。
内容的提问来源于stack exchange,提问作者Abdul
相关产品推荐
相关产品推荐

