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

C# 12主构造函数执行顺序相反问题及替代方案咨询

C# 12主构造函数执行顺序反转问题及替代解决方案

C# 12引入的主构造函数存在执行顺序与经典构造函数相反的情况,这属于破坏性变更,在处理流这类有状态的资源时会直接引发逻辑错误。以下是复现该问题的测试代码与对比结果:

namespace Whatever;

[TestClass]
public class UnitTestTemp
{
    [TestMethod]
    public void TestMethod1() // PASS // 符合预期,第一个值为1,第二个为2
    {
        using var stream = new MemoryStream(new byte[] { 1, 2, 3, 4 });

        var classicDerived = new ClassicDerived(stream);

        Console.WriteLine(classicDerived.Value1);
        Console.WriteLine(classicDerived.Value2);

        Assert.AreEqual(1, classicDerived.Value1);
        Assert.AreEqual(2, classicDerived.Value2);
    }

    [TestMethod]
    public void TestMethod2() // FAIL // 顺序相反,第一个值为2,第二个为1
    {
        using var stream = new MemoryStream(new byte[] { 1, 2, 3, 4 });

        var primaryDerived = new PrimaryDerived(stream);

        Console.WriteLine(primaryDerived.Value1);
        Console.WriteLine(primaryDerived.Value2);

        Assert.AreEqual(1, primaryDerived.Value1);
        Assert.AreEqual(2, primaryDerived.Value2);
    }
}

经典构造函数实现(执行顺序符合预期)

public class ClassicBase
{
    public readonly int Value1;

    protected ClassicBase(Stream stream)
    {
        Value1 = stream.ReadByte();
    }
}

public class ClassicDerived : ClassicBase
{
    public readonly int Value2;

    public ClassicDerived(Stream stream) : base(stream)
    {
        Value2 = stream.ReadByte();
    }
}

主构造函数实现(执行顺序反转)

public class PrimaryBase(Stream stream)
{
    public readonly int Value1 = stream.ReadByte();
}

public class PrimaryDerived(Stream stream) : PrimaryBase(stream)
{
    public readonly int Value2 = stream.ReadByte();
}

测试结果对比

经典构造函数测试结果

TestMethod1
   Source: UnitTestTemp.cs line 7
   Duration: 4 ms

  Standard Output:
1
2

主构造函数测试结果

TestMethod2
   Source: UnitTestTemp.cs line 21
   Duration: 26 ms

  Message:
Assert.AreEqual failed. Expected:<1>. Actual:<2>. 

  Stack Trace:
UnitTestTemp.TestMethod2() line 30
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

  Standard Output:
2
1

可以看到,主构造函数中派生类的字段初始化逻辑先于基类的构造逻辑执行,导致流的读取顺序反转,引发断言失败。


替代解决方案(无需回归经典构造函数)

方案1:将字段初始化逻辑移至主构造函数体中

主构造函数允许显式添加构造体,构造体中的代码会在base调用之后执行,与经典构造函数的执行顺序一致:

public class PrimaryBase(Stream stream)
{
    public readonly int Value1;

    // 主构造函数体,在base调用后执行(基类无父类则直接执行)
    PrimaryBase()
    {
        Value1 = stream.ReadByte();
    }
}

public class PrimaryDerived(Stream stream) : PrimaryBase(stream)
{
    public readonly int Value2;

    // 派生类主构造函数体,在base调用后执行
    PrimaryDerived()
    {
        Value2 = stream.ReadByte();
    }
}

方案2:使用构造函数链显式控制执行顺序

如果需要更灵活的构造逻辑,可以显式定义构造函数并调用主构造函数,确保base调用优先:

public class PrimaryBase(Stream stream)
{
    public readonly int Value1;

    public PrimaryBase(Stream stream, bool dummy) : this(stream)
    {
        Value1 = stream.ReadByte();
    }
}

public class PrimaryDerived(Stream stream) : PrimaryBase(stream, true)
{
    public readonly int Value2;

    public PrimaryDerived(Stream stream, bool dummy) : this(stream)
    {
        Value2 = stream.ReadByte();
    }
}

注:这里的dummy参数仅用于触发构造函数链,实际使用时可根据需求调整参数逻辑。

方案3:利用初始化方法封装逻辑

将资源读取逻辑封装为独立方法,在主构造函数体中调用,确保执行顺序:

public class PrimaryBase(Stream stream)
{
    public readonly int Value1;

    PrimaryBase()
    {
        Value1 = ReadStreamValue(stream);
    }

    private static int ReadStreamValue(Stream stream) => stream.ReadByte();
}

public class PrimaryDerived(Stream stream) : PrimaryBase(stream)
{
    public readonly int Value2;

    PrimaryDerived()
    {
        Value2 = ReadStreamValue(stream);
    }

    private static int ReadStreamValue(Stream stream) => stream.ReadByte();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 19:27:19