C#中new()约束实例化后赋值修改基类隐藏字段的原因咨询
问题:Child.Wrapper的Node字段为何未被赋值,基类字段却被赋值?
调试时观察到Child.Wrapper的实例MyWrapperInstance,仅Base.BaseWrapper的隐藏Node字段被赋值为通过new()构造的Child.Node实例,而MyWrapperInstance.Node仍为null,与预期不符。
代码示例
var MyChildInstance = new Child(); var MyWrapperInstance = MyChildInstance.New(); // 结果为true: var isNull = MyWrapperInstance.Node is null; public class Base { public class BaseNode { } public class BaseWrapper { public BaseNode Node; } public TWrapper MakeNew<TWrapper, TNode>() where TWrapper : BaseWrapper, new() where TNode : BaseNode, new() { var wrapper = new TWrapper() { Node = null }; // 预期赋值派生类属性,但实际修改了基类隐藏字段 wrapper.Node = new TNode(); return wrapper; } } public class Child : Base { public class Node : BaseNode { } public class Wrapper : BaseWrapper { // 隐藏BaseWrapper的字段 public new Node Node; } public Wrapper New() => MakeNew<Wrapper, Node>(); }
环境:Visual Studio 17.3,.NET 6.0控制台应用
原因分析
核心是字段隐藏(new关键字)的编译时绑定特性:
- 在
Base.MakeNew方法中,wrapper的声明类型受泛型约束限制为BaseWrapper,编译器会根据编译时类型解析wrapper.Node,因此实际访问的是BaseWrapper类中的Node字段,而非派生类Wrapper中用new关键字创建的同名新字段。 - 字段不支持多态,
new关键字只是在派生类中生成了一个与基类同名的独立字段,并非覆盖基类成员。编译阶段就会确定要访问的字段,运行时不会根据实例的实际类型动态切换。 - 即便
wrapper的实际类型是Wrapper,但在MakeNew方法的上下文里,编译器仅能识别它是BaseWrapper的子类,因此只会绑定到基类的Node字段。
解决方法
方法1:使用虚属性+重写(推荐,符合多态设计)
将字段改为虚属性,派生类用override重写而非new隐藏:
public class Base { public class BaseNode { } public class BaseWrapper { public virtual BaseNode Node { get; set; } } public TWrapper MakeNew<TWrapper, TNode>() where TWrapper : BaseWrapper, new() where TNode : BaseNode, new() { var wrapper = new TWrapper(); wrapper.Node = new TNode(); return wrapper; } } public class Child : Base { public class Node : BaseNode { } public class Wrapper : BaseWrapper { public override Node Node { get; set; } } public Wrapper New() => MakeNew<Wrapper, Node>(); }
方法2:强制转换为派生类类型访问字段
在MakeNew方法中,将wrapper强制转换为TWrapper后访问派生类的Node字段:
public TWrapper MakeNew<TWrapper, TNode>() where TWrapper : BaseWrapper, new() where TNode : BaseNode, new() { var wrapper = new TWrapper(); // 强制转换后访问派生类的Node字段 ((TWrapper)wrapper).Node = (TNode)new TNode(); return wrapper; }
内容的提问来源于stack exchange,提问作者user266517
相关产品推荐
相关产品推荐

