Skip to content

Commit 2de7f8f

Browse files
release v2.1.1
Nino.Serialization v2.1.1 - [Fix] Fix overheads when deserializing a type with parameterless constructor - [Optimization] Optimize performance when deserializing types with custom parameters, make deserializing field members way faster
1 parent 63a0579 commit 2de7f8f

File tree

6 files changed

+98
-93
lines changed

6 files changed

+98
-93
lines changed

Nino.unitypackage

-167 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
-512 Bytes
Binary file not shown.

src/Nino.Generator/DeserializerGenerator.cs

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ void WriteMembersWithCustomConstructor(List<CSharpSyntaxNode> members, string ty
121121
{
122122
List<(string, string)> vars = new List<(string, string)>();
123123
Dictionary<string, string> args = new Dictionary<string, string>();
124+
bool instantiated = false;
124125
foreach (var memberDeclarationSyntax in members)
125126
{
126127
var name = memberDeclarationSyntax.GetMemberName();
@@ -130,13 +131,21 @@ void WriteMembersWithCustomConstructor(List<CSharpSyntaxNode> members, string ty
130131
if (declaredType == null)
131132
throw new Exception("declaredType is null");
132133

134+
//early exit
135+
if (memberDeclarationSyntax is FieldDeclarationSyntax && instantiated)
136+
{
137+
sb.AppendLine(
138+
$" {declaredType.GetDeserializePrefix()}(out {valName}.{name}, ref reader);");
139+
continue;
140+
}
141+
133142
var t = declaredType.ToDisplayString().Select(c => char.IsLetterOrDigit(c) ? c : '_')
134143
.Aggregate("", (a, b) => a + b);
135144
var tempName = $"{t}_temp_{name}";
136145
sb.AppendLine(
137146
$" {declaredType.GetDeserializePrefix()}(out {declaredType.ToDisplayString()} {tempName}, ref reader);");
138147

139-
if (constructorMember.Any(c => c.ToLower().Equals(name?.ToLower())))
148+
if (constructorMember.Any(c => c.ToLower().Equals(name?.ToLower())) && !instantiated)
140149
{
141150
args.Add(name!, tempName);
142151
}
@@ -148,16 +157,21 @@ void WriteMembersWithCustomConstructor(List<CSharpSyntaxNode> members, string ty
148157
vars.Add((name, tempName)!);
149158
}
150159
}
160+
161+
if (args.Count == constructorMember.Length && !instantiated)
162+
{
163+
sb.AppendLine(
164+
$" {valName} = new {typeName}({string.Join(", ",
165+
constructorMember.Select(m =>
166+
args[args.Keys
167+
.FirstOrDefault(k =>
168+
k.ToLower()
169+
.Equals(m.ToLower()))]
170+
))});");
171+
instantiated = true;
172+
}
151173
}
152174

153-
sb.AppendLine(
154-
$" {valName} = new {typeName}({string.Join(", ",
155-
constructorMember.Select(m =>
156-
args[args.Keys
157-
.FirstOrDefault(k =>
158-
k.ToLower()
159-
.Equals(m.ToLower()))]
160-
))});");
161175
foreach (var (memberName, varName) in vars)
162176
{
163177
sb.AppendLine($" {valName}.{memberName} = {varName};");
@@ -184,10 +198,8 @@ void CreateInstance(List<CSharpSyntaxNode> defaultMembers, INamedTypeSymbol symb
184198

185199
if (constructor == null)
186200
{
187-
sb.AppendLine(
188-
" // fallback to default constructor since no constructor found");
189-
sb.AppendLine($" {valName} = new {typeName}();");
190-
WriteMembers(defaultMembers, valName);
201+
sb.AppendLine(" // no constructor found");
202+
sb.AppendLine($" throw new InvalidOperationException(\"No constructor found for {typeName}\");");
191203
return;
192204
}
193205

@@ -198,12 +210,12 @@ void CreateInstance(List<CSharpSyntaxNode> defaultMembers, INamedTypeSymbol symb
198210
{
199211
constructor = custom;
200212
}
213+
sb.AppendLine($" // use {constructor.ToDisplayString()}");
201214

202215
var attr = constructor.GetNinoConstructorAttribute();
203-
string[]? args;
216+
string[] args;
204217
if (attr != null)
205218
{
206-
sb.AppendLine($" //use {constructor.ToDisplayString()}");
207219
//attr is [NinoConstructor(nameof(a), nameof(b), nameof(c), ...)]
208220
//we need to get a, b, c, ...
209221
var args0 = attr.ConstructorArguments[0].Values;
@@ -215,19 +227,8 @@ void CreateInstance(List<CSharpSyntaxNode> defaultMembers, INamedTypeSymbol symb
215227
{
216228
args = constructor.Parameters.Select(p => p.Name).ToArray();
217229
}
218-
219-
if (args.All(a => defaultMembers.Any(m => m.GetMemberName()?.ToLower() == a.ToLower())))
220-
{
221-
WriteMembersWithCustomConstructor(defaultMembers, typeName, valName, args);
222-
}
223-
else
224-
{
225-
sb.AppendLine($" //use {constructor.ToDisplayString()}");
226-
sb.AppendLine(
227-
$" // fallback to default constructor from {constructor.ToDisplayString()}");
228-
sb.AppendLine($" {valName} = new {typeName}();");
229-
WriteMembers(defaultMembers, valName);
230-
}
230+
231+
WriteMembersWithCustomConstructor(defaultMembers, typeName, valName, args);
231232
}
232233

233234
if (!subTypeMap.TryGetValue(typeFullName, out var lst))

src/Nino.Generator/NinoTypeHelper.cs

Lines changed: 68 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ public static string GetDeserializePrefix(this ITypeSymbol ts)
155155
}
156156

157157
var assName = ts.ContainingAssembly.Name;
158-
var curNamespace = $"{assName!}";
158+
var curNamespace = $"{assName}";
159159
if (!string.IsNullOrEmpty(curNamespace))
160160
curNamespace = $"{curNamespace}_";
161161
if (!char.IsLetter(curNamespace[0]))
@@ -176,7 +176,7 @@ public static string GetSerializePrefix(this ITypeSymbol ts)
176176
}
177177

178178
var assName = ts.ContainingAssembly.Name;
179-
var curNamespace = $"{assName!}";
179+
var curNamespace = $"{assName}";
180180
if (!string.IsNullOrEmpty(curNamespace))
181181
curNamespace = $"{curNamespace}_";
182182
if (!char.IsLetter(curNamespace[0]))
@@ -349,7 +349,7 @@ private static string GetFullTypeName(TypeDeclarationSyntax typeDeclaration, str
349349
switch (memberDeclaration)
350350
{
351351
case PropertyDeclarationSyntax propertyDeclaration:
352-
var propertySymbol = model.GetDeclaredSymbol(propertyDeclaration) as IPropertySymbol;
352+
var propertySymbol = model.GetDeclaredSymbol(propertyDeclaration);
353353
return propertySymbol?.Type;
354354

355355
case FieldDeclarationSyntax fieldDeclaration:
@@ -358,7 +358,7 @@ private static string GetFullTypeName(TypeDeclarationSyntax typeDeclaration, str
358358
return fieldSymbol?.Type;
359359

360360
case ParameterSyntax parameterSyntax:
361-
var parameterSymbol = model.GetDeclaredSymbol(parameterSyntax) as IParameterSymbol;
361+
var parameterSymbol = model.GetDeclaredSymbol(parameterSyntax);
362362
return parameterSymbol?.Type;
363363
}
364364

@@ -402,78 +402,82 @@ public static List<CSharpSyntaxNode> GetNinoTypeMembers(this TypeDeclarationSynt
402402
if (parentNinoTypes != null)
403403
ninoTypes.AddRange(parentNinoTypes);
404404
//get all fields and properties with getter and setter
405-
var ret = ninoTypes.SelectMany(static t => t.Members)
406-
.Where(static m => m is FieldDeclarationSyntax or PropertyDeclarationSyntax { AccessorList: not null })
407-
.Select(static m => m as CSharpSyntaxNode)
408-
.Concat(
409-
//consider record (init only) properties
410-
//i.e. public record Record(int A, string B);, we want to get A and B
411-
ninoTypes.Where(static t => t is RecordDeclarationSyntax)
412-
.Select(static t => t as RecordDeclarationSyntax)
413-
.Where(static r => r != null && r.ParameterList != null)
414-
//now extract the init only properties (A and B) from the record declaration
415-
.SelectMany(static r => r!.ParameterList!.Parameters)
416-
.Where(static p => p.Type != null)
417-
)
418-
.Where(static m => m != null)
419-
.Where(node =>
420-
{
421-
MemberDeclarationSyntax? m = node as MemberDeclarationSyntax;
422-
//has to be public
423-
if (m != null)
405+
var ret =
406+
//consider record (init only) properties
407+
//i.e. public record Record(int A, string B);, we want to get A and B
408+
ninoTypes.Where(static t => t is RecordDeclarationSyntax)
409+
.Select(static t => t as RecordDeclarationSyntax)
410+
.Where(static r => r != null && r.ParameterList != null)
411+
//now extract the init only properties (A and B) from the record declaration
412+
.SelectMany(static r => r!.ParameterList!.Parameters)
413+
.Where(static p => p.Type != null)
414+
.Concat(
415+
ninoTypes
416+
.SelectMany(static t => t.Members)
417+
.Where(static m => m is FieldDeclarationSyntax or PropertyDeclarationSyntax
418+
{
419+
AccessorList: not null
420+
})
421+
.Select(static m => m as CSharpSyntaxNode)
422+
.Where(static m => m != null))
423+
.Where(node =>
424424
{
425-
if (!m.Modifiers.Any(static m => m.Text == "public"))
425+
MemberDeclarationSyntax? m = node as MemberDeclarationSyntax;
426+
//has to be public
427+
if (m != null)
426428
{
427-
return false;
429+
if (!m.Modifiers.Any(static m => m.Text == "public"))
430+
{
431+
return false;
432+
}
428433
}
429-
}
430434

431-
var attrList = m?.AttributeLists ?? ((ParameterSyntax)node).AttributeLists;
435+
var attrList = m?.AttributeLists ?? ((ParameterSyntax)node).AttributeLists;
432436

433-
//if has ninoignore attribute, ignore this member
434-
if (attrList.SelectMany(static al => al.Attributes)
435-
.Any(static a => a.Name.ToString() == "NinoIgnore"))
436-
{
437-
return false;
438-
}
437+
//if has ninoignore attribute, ignore this member
438+
if (attrList.SelectMany(static al => al.Attributes)
439+
.Any(static a => a.Name.ToString() == "NinoIgnore"))
440+
{
441+
return false;
442+
}
439443

440-
var memberName = node.GetMemberName();
441-
if (memberName == null)
442-
{
443-
return false;
444-
}
444+
var memberName = node.GetMemberName();
445+
if (memberName == null)
446+
{
447+
return false;
448+
}
445449

446-
if (autoCollect)
447-
{
448-
memberIndex[memberName] = memberIndex.Count;
449-
return true;
450-
}
450+
if (autoCollect)
451+
{
452+
memberIndex[memberName] = memberIndex.Count;
453+
return true;
454+
}
451455

452456

453-
//get nino member attribute's first argument on this member
454-
var arg = attrList.SelectMany(static al => al.Attributes)
455-
.Where(static a => a.Name.ToString() == "NinoMember")
456-
.Select(static a => a.ArgumentList?.Arguments.FirstOrDefault())
457-
.Select(static a => a?.Expression)
458-
.OfType<LiteralExpressionSyntax>()
459-
.FirstOrDefault();
457+
//get nino member attribute's first argument on this member
458+
var arg = attrList.SelectMany(static al => al.Attributes)
459+
.Where(static a => a.Name.ToString() == "NinoMember")
460+
.Select(static a => a.ArgumentList?.Arguments.FirstOrDefault())
461+
.Select(static a => a?.Expression)
462+
.OfType<LiteralExpressionSyntax>()
463+
.FirstOrDefault();
460464

461-
if (arg == null)
462-
{
463-
return false;
464-
}
465+
if (arg == null)
466+
{
467+
return false;
468+
}
465469

466-
//get index value from NinoMemberAttribute
467-
var indexValue = arg.Token.Value as int?;
468-
if (indexValue == null)
469-
{
470-
return false;
471-
}
470+
//get index value from NinoMemberAttribute
471+
var indexValue = arg.Token.Value as int?;
472+
if (indexValue == null)
473+
{
474+
return false;
475+
}
472476

473-
memberIndex[memberName] = indexValue.Value;
474-
return true;
475-
})
476-
.ToList();
477+
memberIndex[memberName] = indexValue.Value;
478+
return true;
479+
})
480+
.ToList();
477481

478482
//sort by name
479483
ret.Sort((a, b) =>

src/Nino.UnitTests/TestClass.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public record SimpleRecord3(
127127
[NinoMember(2)] string Name,
128128
[NinoMember(1)] DateTime CreateTime)
129129
{
130-
[NinoMember(0)] public bool Flag;
130+
[NinoMember(4)] public bool Flag;
131131

132132
public int Ignored;
133133
}

0 commit comments

Comments
 (0)