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

