Skip to content

Commit 664c097

Browse files
committed
ValueObject comparison operators disallow null.
1 parent f520121 commit 664c097

File tree

3 files changed

+19
-32
lines changed

3 files changed

+19
-32
lines changed

DomainModeling.Generator/ValueObjectGenerator.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,10 @@ public int CompareTo({typeName}? other)
343343
{(existingComponents.HasFlags(ValueObjectTypeComponents.NotEqualsOperator) ? "//" : "")}public static bool operator !=({typeName}? left, {typeName}? right) => !(left == right);
344344
345345
{(isComparable ? "" : "/*")}
346-
{(existingComponents.HasFlags(ValueObjectTypeComponents.GreaterThanOperator) ? "//" : "")}public static bool operator >({typeName}? left, {typeName}? right) => left is null ? false : left.CompareTo(right) > 0;
347-
{(existingComponents.HasFlags(ValueObjectTypeComponents.LessThanOperator) ? "//" : "")}public static bool operator <({typeName}? left, {typeName}? right) => left is null ? right is not null : left.CompareTo(right) < 0;
348-
{(existingComponents.HasFlags(ValueObjectTypeComponents.GreaterEqualsOperator) ? "//" : "")}public static bool operator >=({typeName}? left, {typeName}? right) => !(left < right);
349-
{(existingComponents.HasFlags(ValueObjectTypeComponents.LessEqualsOperator) ? "//" : "")}public static bool operator <=({typeName}? left, {typeName}? right) => !(left > right);
346+
{(existingComponents.HasFlags(ValueObjectTypeComponents.GreaterThanOperator) ? "//" : "")}public static bool operator >({typeName} left, {typeName} right) => left.CompareTo(right) > 0;
347+
{(existingComponents.HasFlags(ValueObjectTypeComponents.LessThanOperator) ? "//" : "")}public static bool operator <({typeName} left, {typeName} right) => left.CompareTo(right) < 0;
348+
{(existingComponents.HasFlags(ValueObjectTypeComponents.GreaterEqualsOperator) ? "//" : "")}public static bool operator >=({typeName} left, {typeName} right) => !(left < right);
349+
{(existingComponents.HasFlags(ValueObjectTypeComponents.LessEqualsOperator) ? "//" : "")}public static bool operator <=({typeName} left, {typeName} right) => !(left > right);
350350
{(isComparable ? "" : "*/")}
351351
}}
352352
}}

DomainModeling.Tests/ValueObjectTests.cs

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -754,52 +754,46 @@ public void CompareTo_WithIgnoreCaseString_ShouldReturnExpectedResult(string? on
754754
}
755755

756756
[Theory]
757-
[InlineData(null, null, 0)]
758-
[InlineData(null, "", -1)]
759-
[InlineData("", null, +1)]
760757
[InlineData("", "", 0)]
761758
[InlineData("", "A", -1)]
762759
[InlineData("A", "", +1)]
763760
[InlineData("A", "a", 0)]
764761
[InlineData("a", "A", 0)]
765762
[InlineData("A", "B", -1)]
766763
[InlineData("AA", "A", +1)]
767-
public void GreaterThan_WithIgnoreCaseString_ShouldReturnExpectedResult(string? one, string? two, int expectedResult)
764+
public void GreaterThan_WithIgnoreCaseString_ShouldReturnExpectedResult(string one, string two, int expectedResult)
768765
{
769-
var left = one is null ? null : new StringValue(one, "7");
770-
var right = two is null ? null : new StringValue(two, "7");
766+
var left = new StringValue(one, "7");
767+
var right = new StringValue(two, "7");
771768

772769
Assert.Equal(expectedResult > 0, left > right);
773770
Assert.Equal(expectedResult <= 0, left <= right);
774771

775-
left = one is null ? null : new StringValue("7", one);
776-
right = two is null ? null : new StringValue("7", two);
772+
left = new StringValue("7", one);
773+
right = new StringValue("7", two);
777774

778775
Assert.Equal(expectedResult > 0, left > right);
779776
Assert.Equal(expectedResult <= 0, left <= right);
780777
}
781778

782779
[Theory]
783-
[InlineData(null, null, 0)]
784-
[InlineData(null, "", -1)]
785-
[InlineData("", null, +1)]
786780
[InlineData("", "", 0)]
787781
[InlineData("", "A", -1)]
788782
[InlineData("A", "", +1)]
789783
[InlineData("A", "a", 0)]
790784
[InlineData("a", "A", 0)]
791785
[InlineData("A", "B", -1)]
792786
[InlineData("AA", "A", +1)]
793-
public void LessThan_WithIgnoreCaseString_ShouldReturnExpectedResult(string? one, string? two, int expectedResult)
787+
public void LessThan_WithIgnoreCaseString_ShouldReturnExpectedResult(string one, string two, int expectedResult)
794788
{
795-
var left = one is null ? null : new StringValue(one, "7");
796-
var right = two is null ? null : new StringValue(two, "7");
789+
var left = new StringValue(one, "7");
790+
var right = new StringValue(two, "7");
797791

798792
Assert.Equal(expectedResult < 0, left < right);
799793
Assert.Equal(expectedResult >= 0, left >= right);
800794

801-
left = one is null ? null : new StringValue("7", one);
802-
right = two is null ? null : new StringValue("7", two);
795+
left = new StringValue("7", one);
796+
right = new StringValue("7", two);
803797

804798
Assert.Equal(expectedResult < 0, left < right);
805799
Assert.Equal(expectedResult >= 0, left >= right);
@@ -816,14 +810,6 @@ public void ComparisonOperators_WithNullValueVsNull_ShouldReturnExpectedResult()
816810
Assert.True(null != nullValued);
817811
Assert.False(nullValued == null);
818812
Assert.True(nullValued != null);
819-
Assert.True(null < nullValued);
820-
Assert.True(null <= nullValued);
821-
Assert.False(null >= nullValued);
822-
Assert.False(null >= nullValued);
823-
Assert.False(nullValued < null);
824-
Assert.False(nullValued <= null);
825-
Assert.True(nullValued > null);
826-
Assert.True(nullValued >= null);
827813
#pragma warning restore xUnit2024 // Do not use boolean asserts for simple equality tests
828814
#pragma warning restore IDE0079 // Remove unnecessary suppressions
829815
}

DomainModeling/DomainModeling.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Platforms:
4040
The base class is dead - long live the interface!
4141
- Feature: Generated Wrappers can now be structs. Unvalidated instances are avoided via analyzer warning against `default`.
4242
- Feature: Generated [Wrapper]ValueObjects can now be records or use custom base.
43-
- BREAKING: [Wrapper]ValueObject generator replaced base class by interface. StringComparison property may need to drop "override" keyword and become private.
43+
- BREAKING: [Wrapper]ValueObject generator replaced base class by interface. StringComparison property should drop "override" and become private.
4444
- BREAKING: Lack of ValueObject base class requires the string validation methods to be accessed via ValueObjectStringValidator class.
4545
- BREAKING: Entity&lt;TId, TPrimitive&gt; type params moved from base class to attribute.
4646

@@ -63,12 +63,13 @@ Performance:
6363
- Enhancement: Source gen performance.
6464

6565
Misc:
66+
- BREAKING: ValueObject comparison operators disallow null.
6667
- Semi-breaking: Entity&lt;TId&gt; now has ID-based ==/!=.
67-
- Semi-breaking: IFormattable &amp; co for string wrappers have stopped treating null strings as "", which covered up mistakes instead of revealing them.
68-
- Semi-breaking: IIdentity now implements IWrapperValueObject.
68+
- Semi-breaking: IFormattable &amp; co for string wrappers have stopped treating null strings as "", to better reveal mistakes.
69+
- Semi-breaking: IIdentity implements IWrapperValueObject.
6970
- Feature: Analyzer and extensions for defined enums.
7071
- Feature: Non-generic Wrapper/Identity interfaces.
71-
- Feature: DummyBuilder records clone on each step, for reuse.
72+
- Feature: DummyBuilder records cloned on each step, for reuse.
7273
- Feature: Analyzer warns when '==' or similar operator implicitly casts some IValueObject to something else. Avoids accidentally comparing unrelated types.
7374
- Feature: Analyzer warns when '>' or similar operator risks unintended null handling.
7475
- Feature: Analyzer warns if field initializers might be skipped.

0 commit comments

Comments
 (0)