From aae1a288951ac68c4f3bd92a2d82365d90cf83ac Mon Sep 17 00:00:00 2001 From: sonyps5201314 Date: Tue, 21 Oct 2025 23:11:42 +0800 Subject: [PATCH] The IsPrimaryConstructor function is reimplemented based on some of dotPeek's judgment rules and the definition and characteristics of the primary constructor. However, it is found that dotPeek's judgment logic is also problematic. For example, it does not support the following scenario: ``` class WebPair(string name, string? value, ref readonly object encode) { public string Name { get; } = name; public string? Value { get; } = value; string? WebValue { get; } = encode is null ? "111" : value; string? WebValue2 { get; } = encode.ToString(); } ``` --- .../CSharp/RecordDecompiler.cs | 58 ++++++++++++++----- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 7a51c1c35..336ac3843 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -188,6 +188,11 @@ namespace ICSharpCode.Decompiler.CSharp return null; + //判定主构造函数的唯一准则是: + //构造函数中前面部分都是类成员属性/字段赋值语句, + //之后是调用基类构造函数的语句即"base..ctor();", + //再之后就是返回语句了,即"base..ctor();"之后不存在任何其他初始化语句。 + //且主构造函数的参数和类成员之间无任何对应关系! bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod) { Debug.Assert(method.IsConstructor); @@ -198,9 +203,10 @@ namespace ICSharpCode.Decompiler.CSharp if (method.Parameters.Count == 0) return false; - for (int i = 0; i < method.Parameters.Count; i++) + List backingMembers = new List(); + foreach (var instruction in body.Instructions) { - if (!body.Instructions[i].MatchStFld(out var target, out var field, out var valueInst)) + if (!instruction.MatchStFld(out var target, out var field, out var valueInst)) break; if (!target.MatchLdThis()) return false; @@ -208,27 +214,39 @@ namespace ICSharpCode.Decompiler.CSharp if (backingFieldToAutoProperty.TryGetValue(field, out var property)) { backingMember = property; + backingMembers.Add(backingMember); } else if (!recordTypeDef.IsRecord) { backingMember = field; + backingMembers.Add(backingMember); } else { return false; } - - primaryCtorParameterToAutoPropertyOrBackingField.Add(unspecializedMethod.Parameters[i], backingMember); - autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, unspecializedMethod.Parameters[i]); } if (!isStruct) { - var baseCtorCall = body.Instructions.SecondToLastOrDefault() as CallInstruction; + Call baseCtorCall; + IsBaseCtorCall(body.Instructions.SecondToLastOrDefault(), out baseCtorCall); if (baseCtorCall == null) return false; } + for (int i = 0; i < method.Parameters.Count; i++) + { + var param = unspecializedMethod.Parameters[i]; + //var backingMember = backingMembers.Find(x => x.Name == param.Name); + var backingMember = backingMembers.Count > i ? backingMembers[i] : null; + if (backingMember != null) + { + primaryCtorParameterToAutoPropertyOrBackingField.Add(param, backingMember); + autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, param); + } + } + var returnInst = body.Instructions.LastOrDefault(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); } @@ -403,6 +421,23 @@ namespace ICSharpCode.Decompiler.CSharp } } + bool IsBaseCtorCall(ILInstruction instruction, out Call _baseCtorCall) + { + _baseCtorCall = null; + + if (!(instruction is Call { Method: { IsConstructor: true } } baseCtorCall)) + return false; + if (!object.Equals(baseCtorCall.Method.DeclaringType, baseClass)) + return false; + if (baseCtorCall.Arguments.Count != (isInheritedRecord ? 2 : 1)) + return false; + if (!baseCtorCall.Arguments[0].MatchLdThis()) + return false; + + _baseCtorCall = baseCtorCall; + return true; + } + private bool IsGeneratedCopyConstructor(IMethod method) { /* @@ -425,14 +460,11 @@ namespace ICSharpCode.Decompiler.CSharp Debug.Assert(IsRecordType(other.Type)); int pos = 0; // First instruction is the base constructor call - if (!(body.Instructions[pos] is Call { Method: { IsConstructor: true } } baseCtorCall)) - return false; - if (!object.Equals(baseCtorCall.Method.DeclaringType, baseClass)) - return false; - if (baseCtorCall.Arguments.Count != (isInheritedRecord ? 2 : 1)) - return false; - if (!baseCtorCall.Arguments[0].MatchLdThis()) + Call baseCtorCall; + if (!IsBaseCtorCall(body.Instructions[pos], out baseCtorCall)) + { return false; + } if (isInheritedRecord) { if (!baseCtorCall.Arguments[1].MatchLdLoc(other))