Skip to content

Commit 30c0151

Browse files
committed
implement reference comparer, improve tests
1 parent 92c5f8d commit 30c0151

File tree

12 files changed

+490
-38
lines changed

12 files changed

+490
-38
lines changed

src/Equatable.SourceGenerator/EquatableGenerator.cs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Reflection;
12
using System.Xml.Linq;
23

34
using Equatable.SourceGenerator.Models;
@@ -200,11 +201,15 @@ private static (ComparerTypes? comparerType, string? comparerName, string? compa
200201

201202
private static (ComparerTypes? comparerType, string? comparerName, string? comparerInstance) GetStringComparer(AttributeData? attribute)
202203
{
203-
var argument = attribute?.ConstructorArguments.FirstOrDefault();
204-
if (argument == null || !argument.HasValue)
204+
if (attribute == null || attribute.ConstructorArguments.Length != 1)
205+
return (ComparerTypes.Default, null, null);
206+
207+
var argument = attribute.ConstructorArguments[0];
208+
209+
if (argument.Value is not int value)
205210
return (ComparerTypes.String, "CurrentCulture", null);
206211

207-
var comparerName = argument?.Value switch
212+
var comparerName = value switch
208213
{
209214
0 => "CurrentCulture",
210215
1 => "CurrentCultureIgnoreCase",
@@ -220,30 +225,19 @@ private static (ComparerTypes? comparerType, string? comparerName, string? compa
220225

221226
private static (ComparerTypes? comparerType, string? comparerName, string? comparerInstance) GetEqualityComparer(AttributeData? attribute)
222227
{
223-
if (attribute == null)
228+
if (attribute == null || attribute.ConstructorArguments.Length != 2)
224229
return (ComparerTypes.Default, null, null);
225230

226-
// attribute constructor
227-
var comparerType = attribute.ConstructorArguments.FirstOrDefault();
228-
if (comparerType.Value is INamedTypeSymbol typeSymbol)
229-
{
230-
return (ComparerTypes.Custom, typeSymbol.ToDisplayString(), null);
231-
}
232-
233-
// generic attribute
234-
var attributeClass = attribute.AttributeClass;
235-
if (attributeClass is { IsGenericType: true }
236-
&& attributeClass.TypeArguments.Length == attributeClass.TypeParameters.Length
237-
&& attributeClass.TypeArguments.Length == 1)
238-
{
239-
var typeArgument = attributeClass.TypeArguments[0];
240-
var comparerName = typeArgument.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
231+
var comparerArgument = attribute.ConstructorArguments[0];
232+
if (comparerArgument.Value is not INamedTypeSymbol typeSymbol)
233+
return (ComparerTypes.Default, null, null); // invalid syntax found
241234

242-
return (ComparerTypes.Custom, comparerName, null);
243-
}
235+
var comparerName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
244236

237+
var instanceArgument = attribute.ConstructorArguments[1];
238+
var comparerInstance = instanceArgument.Value as string;
245239

246-
return (ComparerTypes.Default, null, null);
240+
return (ComparerTypes.Custom, comparerName, comparerInstance);
247241
}
248242

249243

src/Equatable.SourceGenerator/EquatableWriter.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ private static void GenerateEquatable(IndentedStringBuilder codeBuilder, Equatab
124124

125125
break;
126126
case ComparerTypes.Reference:
127+
codeBuilder
128+
.Append(" global::System.Object.ReferenceEquals(")
129+
.Append(entityProperty.PropertyName)
130+
.Append(", other.")
131+
.Append(entityProperty.PropertyName)
132+
.Append(")");
133+
127134
break;
128135
case ComparerTypes.Sequence:
129136
codeBuilder
@@ -395,6 +402,11 @@ private static void GenerateHashCode(IndentedStringBuilder codeBuilder, Equatabl
395402
.AppendLine(");");
396403
break;
397404
case ComparerTypes.Reference:
405+
codeBuilder
406+
.Append("hashCode = (hashCode * -1521134295) + ")
407+
.Append("global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(")
408+
.Append(entityProperty.PropertyName)
409+
.AppendLine("!);");
398410
break;
399411
case ComparerTypes.Sequence:
400412
codeBuilder

test/Equatable.Entities/Audit.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32

43
using Equatable.Attributes;
54

@@ -13,4 +12,7 @@ public partial class Audit : ModelBase
1312
public int? TaskId { get; set; }
1413
public string? Content { get; set; }
1514
public string? UserName { get; set; }
15+
16+
[ReferenceEquality]
17+
public object? Lock { get; set; }
1618
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System.Collections.Generic;
2+
3+
using Equatable.Attributes;
4+
5+
namespace Equatable.Entities;
6+
7+
[Equatable]
8+
public partial class CustomLength
9+
{
10+
public int Id { get; set; }
11+
12+
public string Name { get; set; } = null!;
13+
14+
[EqualityComparer(typeof(LengthComparerDefault))]
15+
public string? Key { get; set; }
16+
17+
[EqualityComparer(typeof(LengthComparerInstance), nameof(LengthComparerInstance.Instance))]
18+
public string? Value { get; set; }
19+
20+
}
21+
22+
public static class LengthComparerDefault
23+
{
24+
public static readonly LengthEqualityComparer Default = new();
25+
}
26+
27+
public static class LengthComparerInstance
28+
{
29+
public static readonly LengthEqualityComparer Instance = new();
30+
}
31+
32+
public class LengthEqualityComparer : IEqualityComparer<string?>
33+
{
34+
public bool Equals(string? x, string? y) => x?.Length == y?.Length;
35+
36+
public int GetHashCode(string? obj) => obj?.Length.GetHashCode() ?? 0;
37+
}
38+

test/Equatable.Entities/Equatable.Entities.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
99
</PropertyGroup>
1010

11+
<ItemGroup>
12+
<PackageReference Include="System.Text.Json" Version="8.0.4" />
13+
</ItemGroup>
14+
1115
<ItemGroup>
1216
<ProjectReference Include="..\..\src\Equatable.Generator\Equatable.Generator.csproj" />
1317
<ProjectReference Include="..\..\src\Equatable.SourceGenerator\Equatable.SourceGenerator.csproj">

test/Equatable.Entities/Nested.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
namespace Equatable.Entities;
44

5-
public class Nested
5+
public partial class Nested
66
{
77
//[Equatable]
88
public partial class Animal
99
{
1010
public int Id { get; set; }
11-
public string Name { get; set; }
12-
public string Type { get; set; }
11+
public string? Name { get; set; }
12+
public string? Type { get; set; }
1313
}
1414
}

test/Equatable.Entities/UserImport.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Text.Json.Serialization;
4+
35
using Equatable.Attributes;
46

57
namespace Equatable.Entities;
@@ -10,6 +12,7 @@ public partial class UserImport
1012
[StringEquality(StringComparison.OrdinalIgnoreCase)]
1113
public string EmailAddress { get; set; } = null!;
1214

15+
[JsonPropertyName("name")]
1316
public string? DisplayName { get; set; }
1417

1518
public string? FirstName { get; set; }
@@ -20,6 +23,7 @@ public partial class UserImport
2023

2124
public DateTimeOffset? LastLogin { get; set; }
2225

26+
[JsonIgnore]
2327
[IgnoreEquality]
2428
public string FullName => $"{FirstName} {LastName}";
2529

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using Equatable.Entities;
2+
3+
namespace Equatable.Generator.Tests.Entities;
4+
5+
public class AuditTest
6+
{
7+
[Fact]
8+
public void EqualAuditTrue()
9+
{
10+
var lockObject = new object();
11+
12+
var left = new Audit
13+
{
14+
Id = 1,
15+
Date = new DateTime(2024, 9, 1),
16+
UserId = 1,
17+
TaskId = 2,
18+
Lock = lockObject
19+
};
20+
21+
var right = new Audit
22+
{
23+
Id = 1,
24+
Date = new DateTime(2024, 9, 1),
25+
UserId = 1,
26+
TaskId = 2,
27+
Lock = lockObject
28+
};
29+
30+
var isEqual = left.Equals(right);
31+
isEqual.Should().BeTrue();
32+
33+
// check operator ==
34+
isEqual = left == right;
35+
isEqual.Should().BeTrue();
36+
}
37+
38+
[Fact]
39+
public void NotEqualAudit()
40+
{
41+
var left = new Audit
42+
{
43+
Id = 1,
44+
Date = new DateTime(2024, 9, 1),
45+
UserId = 1,
46+
TaskId = 2,
47+
Lock = new object()
48+
};
49+
50+
var right = new Audit
51+
{
52+
Id = 1,
53+
Date = new DateTime(2024, 9, 1),
54+
UserId = 1,
55+
TaskId = 2,
56+
Lock = new object()
57+
};
58+
59+
var isEqual = left.Equals(right);
60+
isEqual.Should().BeFalse();
61+
62+
// check operator !=
63+
isEqual = left != right;
64+
isEqual.Should().BeTrue();
65+
66+
}
67+
68+
[Fact]
69+
public void HashCodeAuditTrue()
70+
{
71+
var lockObject = new object();
72+
var left = new Audit
73+
{
74+
Id = 1,
75+
Date = new DateTime(2024, 9, 1),
76+
UserId = 1,
77+
TaskId = 2,
78+
Lock = lockObject
79+
};
80+
81+
var right = new Audit
82+
{
83+
Id = 1,
84+
Date = new DateTime(2024, 9, 1),
85+
UserId = 1,
86+
TaskId = 2,
87+
Lock = lockObject
88+
};
89+
90+
var leftCode = left.GetHashCode();
91+
var rightCode = right.GetHashCode();
92+
93+
leftCode.Should().Be(rightCode);
94+
}
95+
96+
[Fact]
97+
public void HashCodeAuditNotEqual()
98+
{
99+
var left = new Audit
100+
{
101+
Id = 1,
102+
Date = new DateTime(2024, 9, 1),
103+
UserId = 1,
104+
TaskId = 2,
105+
Lock = new object()
106+
};
107+
108+
var right = new Audit
109+
{
110+
Id = 1,
111+
Date = new DateTime(2024, 9, 1),
112+
UserId = 1,
113+
TaskId = 2,
114+
Lock = new object()
115+
};
116+
117+
var leftCode = left.GetHashCode();
118+
var rightCode = right.GetHashCode();
119+
120+
leftCode.Should().NotBe(rightCode);
121+
}
122+
}

0 commit comments

Comments
 (0)