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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 09:10:44