diff --git a/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj b/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj
index 552ed73..e127dbd 100644
--- a/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj
+++ b/ModernUO.Serialization.Annotations/ModernUO.Serialization.Annotations.csproj
@@ -4,8 +4,8 @@
ModernUO.Serialization.Annotations
netstandard2.0
preview
- 4.2.0
- 4.2.0
+ 4.3.0
+ 4.3.0
ModernUO.Serialization.Annotations
ModernUO.Serialization
true
diff --git a/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs b/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs
index faa2c75..eb3893f 100644
--- a/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs
+++ b/ModernUO.Serialization.Generator.Tests/DataStructureMethodTests.cs
@@ -7,14 +7,19 @@ namespace ModernUO.Serialization.Generator.Tests;
///
/// Runs the generated AddToX/RemoveFromX/InsertIntoX/ReplaceInX/ClearX helpers against the
/// Snapshots/NullableCollections entity: removals and clears tolerate a null collection, adds create
-/// it lazily (keeping a [SortedSetComparer]), and MarkDirty fires only when the collection changed.
+/// it lazily (keeping a [SortedSetComparer]), MarkDirty fires only when the collection changed, and
+/// helpers that can be a no-op return whether they changed anything.
///
public class DataStructureMethodTests
{
- private static string FixtureSource([CallerFilePath] string thisFile = "") =>
- File.ReadAllText(
- Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", "NullableCollections", "Input.cs")
- );
+ private static string FixtureSource(string snapshot, [CallerFilePath] string thisFile = "") =>
+ File.ReadAllText(Path.Combine(Path.GetDirectoryName(thisFile)!, "Snapshots", snapshot, "Input.cs"));
+
+ private static string Run(string assemblyName, string source) =>
+ (string)SourceGeneratorTestHelper.CompileAndLoad(assemblyName, source)
+ .GetType("Server.TestContent.Driver")!
+ .GetMethod("Run")!
+ .Invoke(null, null)!;
private const string Driver = """
namespace Server.TestContent
@@ -28,42 +33,37 @@ public static string Run()
void Step(string name, object? state = null) =>
log.Append(name).Append('=').Append(item.DirtyCount).Append(state != null ? $":{state}" : "").Append(';');
- item.RemoveFromCharges(1);
+ var removed = item.RemoveFromCharges(1) | item.RemoveFromKeywords("a") |
+ item.RemoveFromLabels(1) | item.RemoveFromNames("a");
item.RemoveFromChargesAt(0);
item.ClearCharges();
- item.RemoveFromKeywords("a");
item.ClearKeywords();
- item.RemoveFromLabels(1);
item.ClearLabels();
- item.RemoveFromNames("a");
item.ClearNames();
- Step("null", item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null);
+ Step("null", $"{removed}:{item.Charges == null && item.Keywords == null && item.Labels == null && item.Names == null}");
item.AddToCharges(5);
Step("addList");
item.InsertIntoCharges(0, 4);
Step("insert", string.Join(",", item.Charges!));
- item.AddToKeywords("a");
- Step("addSet");
- item.AddToKeywords("a");
- Step("addSetDuplicate");
+ Step("addSet", item.AddToKeywords("a"));
+ Step("addSetDuplicate", item.AddToKeywords("a"));
- item.AddToLabels(1, "x");
- Step("addDictionary");
- item.AddToLabels(1, "y");
- Step("addDictionaryDuplicate", item.Labels![1]);
+ Step("addDictionary", item.AddToLabels(1, "x"));
+ Step("addDictionaryDuplicate", $"{item.AddToLabels(1, "y")}:{item.Labels![1]}");
item.ReplaceInLabels(1, null);
Step("replace", item.Labels[1] ?? "null");
item.AddToNames("Bob");
- item.AddToNames("BOB");
- Step("comparer", item.Names!.Count);
+ Step("comparer", $"{item.AddToNames("BOB")}:{item.Names!.Count}");
- item.RemoveFromKeywords("missing");
- Step("removeMissing");
- item.RemoveFromKeywords("a");
- Step("remove");
+ Step("removeMissing", item.RemoveFromKeywords("missing"));
+ Step("remove", item.RemoveFromKeywords("a"));
+ Step("removeListMissing", item.RemoveFromCharges(9));
+ Step("removeList", item.RemoveFromCharges(4));
+ Step("removeDictionaryMissing", item.RemoveFromLabels(9));
+ Step("removeDictionary", item.RemoveFromLabels(1));
item.ClearKeywords();
Step("clearEmpty");
item.ClearCharges();
@@ -79,20 +79,61 @@ void Step(string name, object? state = null) =>
}
""";
+ // Without a dirty target the helpers still report whether they changed anything.
+ private const string UntrackedSource = """
+ using System.Collections.Generic;
+ using ModernUO.Serialization;
+
+ namespace Server.TestContent
+ {
+ [SerializationGenerator(0)]
+ public partial class UntrackedRecord
+ {
+ [SerializableField(0)]
+ private List _values = new();
+
+ [SerializableField(1)]
+ private HashSet _set = new();
+
+ [SerializableField(2)]
+ private Dictionary _lookup = new();
+ }
+
+ public static class Driver
+ {
+ public static string Run()
+ {
+ var r = new UntrackedRecord();
+ r.AddToValues(1);
+ return string.Join(
+ ",",
+ r.RemoveFromValues(2), r.RemoveFromValues(1),
+ r.AddToSet(1), r.AddToSet(1), r.RemoveFromSet(2), r.RemoveFromSet(1),
+ r.AddToLookup(1, "a"), r.AddToLookup(1, "b"), r.RemoveFromLookup(2), r.RemoveFromLookup(1)
+ );
+ }
+ }
+ }
+ """;
+
[Fact]
public void Helpers_TolerateNullAndMarkDirtyOnlyOnChange()
{
- var assembly = SourceGeneratorTestHelper.CompileAndLoad("NullableCollections", FixtureSource() + Driver);
-
- var result = (string)assembly.GetType("Server.TestContent.Driver")!
- .GetMethod("Run")!
- .Invoke(null, null)!;
+ Assert.Equal(
+ "null=0:False:True;addList=1;insert=2:4,5;addSet=3:True;addSetDuplicate=3:False;addDictionary=4:True;" +
+ "addDictionaryDuplicate=4:False:x;replace=5:null;comparer=6:False:1;removeMissing=6:False;remove=7:True;" +
+ "removeListMissing=7:False;removeList=8:True;removeDictionaryMissing=8:False;removeDictionary=9:True;" +
+ "clearEmpty=9;clear=10:0;replaceNull=1:z",
+ Run("NullableCollections", FixtureSource("NullableCollections") + Driver)
+ );
+ }
+ [Fact]
+ public void Helpers_ReportChangesWithoutADirtyTarget()
+ {
Assert.Equal(
- "null=0:True;addList=1;insert=2:4,5;addSet=3;addSetDuplicate=3;addDictionary=4;" +
- "addDictionaryDuplicate=4:x;replace=5:null;comparer=6:1;removeMissing=6;remove=7;" +
- "clearEmpty=7;clear=8:0;replaceNull=1:z",
- result
+ "False,True,True,False,False,True,True,False,False,True",
+ Run("UntrackedRecord", UntrackedSource)
);
}
}
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs
index d239fea..20db23d 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/Collections/Expected/Server.TestContent.CollectionsItem.Serialization.g.cs
@@ -55,12 +55,15 @@ public void AddToCharges(int value)
Server.ISerializableExtensions.MarkDirty(this);
}
- public void RemoveFromCharges(int value)
+ public bool RemoveFromCharges(int value)
{
if (_charges?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void InsertIntoCharges(int index, int value)
@@ -101,21 +104,27 @@ public System.Collections.Generic.HashSet Keywords
}
}
- public void AddToKeywords(string value)
+ public bool AddToKeywords(string value)
{
_keywords ??= new System.Collections.Generic.HashSet();
if (_keywords.Add(value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromKeywords(string value)
+ public bool RemoveFromKeywords(string value)
{
if (_keywords?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ClearKeywords()
@@ -140,21 +149,27 @@ public System.Collections.Generic.Dictionary Labels
}
}
- public void AddToLabels(int key, string value)
+ public bool AddToLabels(int key, string value)
{
_labels ??= new System.Collections.Generic.Dictionary();
if (_labels.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromLabels(int key)
+ public bool RemoveFromLabels(int key)
{
if (_labels?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ReplaceInLabels(int key, string value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs
index d5f7a73..210c0f4 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/FieldModifiers/Expected/Server.TestContent.FieldModifiersItem.Serialization.g.cs
@@ -41,21 +41,27 @@ public System.Collections.Generic.Dictionary Entries
}
}
- public void AddToEntries(int key, string value)
+ public bool AddToEntries(int key, string value)
{
_entries ??= new System.Collections.Generic.Dictionary();
if (_entries.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromEntries(int key)
+ public bool RemoveFromEntries(int key)
{
if (_entries?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ReplaceInEntries(int key, string value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs
index 4be42ac..8e3b85d 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.ExtendedOwner.Serialization.g.cs
@@ -35,12 +35,15 @@ public void AddToExtendedEntries(Server.TestContent.DerivedEntry value)
Server.ISerializableExtensions.MarkDirty(this);
}
- public void RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value)
+ public bool RemoveFromExtendedEntries(Server.TestContent.DerivedEntry value)
{
if (_extendedEntries?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void InsertIntoExtendedEntries(int index, Server.TestContent.DerivedEntry value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs
index c168f12..631ca1e 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NestedInheritance/Expected/Server.TestContent.Owner.Serialization.g.cs
@@ -35,12 +35,15 @@ public void AddToEntries(Server.TestContent.BaseEntry value)
Server.ISerializableExtensions.MarkDirty(this);
}
- public void RemoveFromEntries(Server.TestContent.BaseEntry value)
+ public bool RemoveFromEntries(Server.TestContent.BaseEntry value)
{
if (_entries?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void InsertIntoEntries(int index, Server.TestContent.BaseEntry value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs
index 1c9da54..232aca7 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NoDirtyTarget/Expected/Server.TestContent.NoDirtyTargetRecord.Serialization.g.cs
@@ -45,9 +45,9 @@ public void AddToValues(int value)
_values.Add(value);
}
- public void RemoveFromValues(int value)
+ public bool RemoveFromValues(int value)
{
- _values?.Remove(value);
+ return _values?.Remove(value) == true;
}
public void InsertIntoValues(int index, int value)
@@ -84,15 +84,15 @@ public System.Collections.Generic.Dictionary Lookup
}
}
- public void AddToLookup(int key, string value)
+ public bool AddToLookup(int key, string value)
{
_lookup ??= new System.Collections.Generic.Dictionary();
- _lookup.TryAdd(key, value);
+ return _lookup.TryAdd(key, value);
}
- public void RemoveFromLookup(int key)
+ public bool RemoveFromLookup(int key)
{
- _lookup?.Remove(key);
+ return _lookup?.Remove(key) == true;
}
public void ReplaceInLookup(int key, string value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs
index fae98bc..278b34f 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableCollections/Expected/Server.TestContent.NullableCollectionsItem.Serialization.g.cs
@@ -48,12 +48,15 @@ public void AddToCharges(int value)
MarkDirty();
}
- public void RemoveFromCharges(int value)
+ public bool RemoveFromCharges(int value)
{
if (_charges?.Remove(value) == true)
{
MarkDirty();
+ return true;
}
+
+ return false;
}
public void InsertIntoCharges(int index, int value)
@@ -94,21 +97,27 @@ public System.Collections.Generic.HashSet? Keywords
}
}
- public void AddToKeywords(string value)
+ public bool AddToKeywords(string value)
{
_keywords ??= new System.Collections.Generic.HashSet();
if (_keywords.Add(value))
{
MarkDirty();
+ return true;
}
+
+ return false;
}
- public void RemoveFromKeywords(string value)
+ public bool RemoveFromKeywords(string value)
{
if (_keywords?.Remove(value) == true)
{
MarkDirty();
+ return true;
}
+
+ return false;
}
public void ClearKeywords()
@@ -133,21 +142,27 @@ public void ClearKeywords()
}
}
- public void AddToLabels(int key, string? value)
+ public bool AddToLabels(int key, string? value)
{
_labels ??= new System.Collections.Generic.Dictionary();
if (_labels.TryAdd(key, value))
{
MarkDirty();
+ return true;
}
+
+ return false;
}
- public void RemoveFromLabels(int key)
+ public bool RemoveFromLabels(int key)
{
if (_labels?.Remove(key) == true)
{
MarkDirty();
+ return true;
}
+
+ return false;
}
public void ReplaceInLabels(int key, string? value)
@@ -179,21 +194,27 @@ public System.Collections.Generic.SortedSet? Names
}
}
- public void AddToNames(string value)
+ public bool AddToNames(string value)
{
_names ??= new System.Collections.Generic.SortedSet(new Server.TestContent.CaseInsensitiveComparer());
if (_names.Add(value))
{
MarkDirty();
+ return true;
}
+
+ return false;
}
- public void RemoveFromNames(string value)
+ public bool RemoveFromNames(string value)
{
if (_names?.Remove(value) == true)
{
MarkDirty();
+ return true;
}
+
+ return false;
}
public void ClearNames()
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs
index 4764128..d6e5800 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/NullableValueTypes/Expected/Server.TestContent.NullableValuesItem.Serialization.g.cs
@@ -113,12 +113,15 @@ public void AddToSamples(int? value)
Server.ISerializableExtensions.MarkDirty(this);
}
- public void RemoveFromSamples(int? value)
+ public bool RemoveFromSamples(int? value)
{
if (_samples?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void InsertIntoSamples(int index, int? value)
@@ -159,21 +162,27 @@ public void ClearSamples()
}
}
- public void AddToWeights(int key, double? value)
+ public bool AddToWeights(int key, double? value)
{
_weights ??= new System.Collections.Generic.Dictionary();
if (_weights.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromWeights(int key)
+ public bool RemoveFromWeights(int key)
{
if (_weights?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ReplaceInWeights(int key, double? value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs
index 5e4940d..8b06cfe 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/ParentCtorPreferred/Expected/Server.TestContent.Roster.Serialization.g.cs
@@ -35,12 +35,15 @@ public void AddToEntries(Server.TestContent.RosterEntry value)
Server.ISerializableExtensions.MarkDirty(this);
}
- public void RemoveFromEntries(Server.TestContent.RosterEntry value)
+ public bool RemoveFromEntries(Server.TestContent.RosterEntry value)
{
if (_entries?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void InsertIntoEntries(int index, Server.TestContent.RosterEntry value)
@@ -81,21 +84,27 @@ public void ClearEntries()
}
}
- public void AddToById(int key, Server.TestContent.RosterEntry value)
+ public bool AddToById(int key, Server.TestContent.RosterEntry value)
{
_byId ??= new System.Collections.Generic.Dictionary();
if (_byId.TryAdd(key, value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromById(int key)
+ public bool RemoveFromById(int key)
{
if (_byId?.Remove(key) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ReplaceInById(int key, Server.TestContent.RosterEntry value)
diff --git a/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs b/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs
index 1b77384..b9f0883 100644
--- a/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs
+++ b/ModernUO.Serialization.Generator.Tests/Snapshots/SortedSetWithComparer/Expected/Server.TestContent.SortedSetItem.Serialization.g.cs
@@ -28,21 +28,27 @@ public System.Collections.Generic.SortedSet Names
}
}
- public void AddToNames(string value)
+ public bool AddToNames(string value)
{
_names ??= new System.Collections.Generic.SortedSet(new Server.TestContent.CaseInsensitiveComparer());
if (_names.Add(value))
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
- public void RemoveFromNames(string value)
+ public bool RemoveFromNames(string value)
{
if (_names?.Remove(value) == true)
{
Server.ISerializableExtensions.MarkDirty(this);
+ return true;
}
+
+ return false;
}
public void ClearNames()
diff --git a/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj b/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj
index d09c2d1..41b305e 100755
--- a/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj
+++ b/ModernUO.Serialization.Generator/ModernUO.Serialization.Generator.csproj
@@ -4,8 +4,8 @@
ModernUO.Serialization.Generator
netstandard2.0
preview
- 4.2.0
- 4.2.0
+ 4.3.0
+ 4.3.0
ModernUO.Serialization.Generator
true
false
diff --git a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs
index 262086b..1934a42 100644
--- a/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs
+++ b/ModernUO.Serialization.Generator/SerializableEntityGeneration/SerializableEntityGeneration.DataStructure.cs
@@ -21,21 +21,22 @@ private static void AppendLazyCreate(this StringBuilder source, string indent, F
}
}
- // Marks dirty only when the mutation reports a change. Without a dirty target, just mutates.
- private static void AppendIfChanged(
- this StringBuilder source, string indent, string changed, string? markDirtyMethod, string? statement = null
- )
+ // Returns whether the mutation reported a change, marking dirty only when it did.
+ private static void AppendReturnIfChanged(this StringBuilder source, string indent, string changed, string? markDirtyMethod)
{
if (markDirtyMethod == null)
{
- source.AppendLine($"{indent} {statement ?? changed};");
+ source.AppendLine($"{indent} return {changed};");
return;
}
source.AppendLine($"{indent} if ({changed})");
source.AppendLine($"{indent} {{");
source.AppendLine($"{indent} {markDirtyMethod};");
+ source.AppendLine($"{indent} return true;");
source.AppendLine($"{indent} }}");
+ source.AppendLine();
+ source.AppendLine($"{indent} return false;");
}
public static bool GenerateDataStructureMethods(
@@ -59,34 +60,38 @@ public static bool GenerateDataStructureMethods(
// Mutations go through the backing field: lazily creating the collection through the
// property setter would run fieldChanged/allowFieldChange for what is not a replacement.
// Removals and clears tolerate a null collection, and only mark dirty on an actual change.
+ // Return types mirror the BCL: a mutation that can be a no-op returns whether it changed anything.
if (field.DsIsDictionary)
{
var valueTypeName = field.DsValueType;
// Add
- source.AppendLine($"{indent}{propertyAccessor} void AddTo{propertyName}({elementTypeName} key, {valueTypeName} value)");
+ source.AppendLine($"{indent}{propertyAccessor} bool AddTo{propertyName}({elementTypeName} key, {valueTypeName} value)");
source.AppendLine($"{indent}{{");
source.AppendLazyCreate(indent, field);
if (field.DsHasTryAdd)
{
- source.AppendIfChanged(indent, $"{fieldName}.TryAdd(key, value)", markDirtyMethod);
+ source.AppendReturnIfChanged(indent, $"{fieldName}.TryAdd(key, value)", markDirtyMethod);
}
else
{
- source.AppendLine($"{indent} if (!{fieldName}.ContainsKey(key))");
+ source.AppendLine($"{indent} if ({fieldName}.ContainsKey(key))");
source.AppendLine($"{indent} {{");
- source.AppendLine($"{indent} {fieldName}.Add(key, value);");
- source.AppendMarkDirty($"{indent} ", markDirtyMethod);
+ source.AppendLine($"{indent} return false;");
source.AppendLine($"{indent} }}");
+ source.AppendLine();
+ source.AppendLine($"{indent} {fieldName}.Add(key, value);");
+ source.AppendMarkDirty(indent, markDirtyMethod);
+ source.AppendLine($"{indent} return true;");
}
source.AppendLine($"{indent}}}");
source.AppendLine();
// Remove
- source.AppendLine($"{indent}{propertyAccessor} void RemoveFrom{propertyName}({elementTypeName} key)");
+ source.AppendLine($"{indent}{propertyAccessor} bool RemoveFrom{propertyName}({elementTypeName} key)");
source.AppendLine($"{indent}{{");
- source.AppendIfChanged(indent, $"{fieldName}?.Remove(key) == true", markDirtyMethod, $"{fieldName}?.Remove(key)");
+ source.AppendReturnIfChanged(indent, $"{fieldName}?.Remove(key) == true", markDirtyMethod);
source.AppendLine($"{indent}}}");
source.AppendLine();
@@ -102,12 +107,13 @@ public static bool GenerateDataStructureMethods(
else if (field.DsIsCollection)
{
// Add
- source.AppendLine($"{indent}{propertyAccessor} void AddTo{propertyName}({elementTypeName} value)");
+ var addReturnType = field.DsIsSet ? "bool" : "void";
+ source.AppendLine($"{indent}{propertyAccessor} {addReturnType} AddTo{propertyName}({elementTypeName} value)");
source.AppendLine($"{indent}{{");
source.AppendLazyCreate(indent, field);
if (field.DsIsSet)
{
- source.AppendIfChanged(indent, $"{fieldName}.Add(value)", markDirtyMethod);
+ source.AppendReturnIfChanged(indent, $"{fieldName}.Add(value)", markDirtyMethod);
}
else
{
@@ -119,9 +125,9 @@ public static bool GenerateDataStructureMethods(
source.AppendLine();
// Remove
- source.AppendLine($"{indent}{propertyAccessor} void RemoveFrom{propertyName}({elementTypeName} value)");
+ source.AppendLine($"{indent}{propertyAccessor} bool RemoveFrom{propertyName}({elementTypeName} value)");
source.AppendLine($"{indent}{{");
- source.AppendIfChanged(indent, $"{fieldName}?.Remove(value) == true", markDirtyMethod, $"{fieldName}?.Remove(value)");
+ source.AppendReturnIfChanged(indent, $"{fieldName}?.Remove(value) == true", markDirtyMethod);
source.AppendLine($"{indent}}}");
}
diff --git a/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj b/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj
index e19d7b6..11cc681 100755
--- a/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj
+++ b/ModernUO.Serialization.SchemaGenerator/ModernUO.Serialization.SchemaGenerator.csproj
@@ -6,8 +6,8 @@
x64;arm64
preview
Exe
- 4.2.0
- 4.2.0
+ 4.3.0
+ 4.3.0
true
ModernUOSchemaGenerator
true
diff --git a/README.md b/README.md
index c5783fe..34bc107 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,8 @@ While it is not the most elegant solution (recommendations and contributions are
Add `ModernUO.Serialization.Generator` and `ModernUO.Serialization.Annotations` as package references:
```xml
-
-
+
+
```
@@ -128,6 +128,9 @@ They tolerate a null collection: adds create it on first use (a `[SortedSetCompa
do nothing. They mark the entity dirty only when the collection actually changed, so adding a duplicate to a set or
dictionary, or removing a missing entry, does not.
+`RemoveFromX`, and `AddToX` on a set or dictionary, return whether the collection changed, like the BCL's `Remove`,
+`HashSet.Add` and `TryAdd`. The rest return `void`.
+
Nullable reference annotations (`string?`, `List- ?`) are ignored for serialization. Nullable value types
(`int?`, `Point3D?`, a nullable `[SerializationGenerator]` struct, or `List`) are written as a `bool` followed by the
value when present.