Equatable.Generator by Eden Prairie
Nuget / site data
Details
Info
Name: Equatable.Generator
Source generator for Equals and GetHashCode
Author: Eden Prairie
NuGet: https://www.nuget.org/packages/Equatable.Generator/
You can find more details at https://github.com/loresoft/Equatable.Generator
Original Readme
Equatable.Generator
Source generator for Equals
and GetHashCode
with attribute based control of equality implementation
Features
- Override
Equals
andGetHashCode
- Implement
IEquatable<T>
- Support
class
,record
andstruct
types - Support
EqualityComparer
per property via attribute - Attribute based control of equality implementation.
- Attribute comparers supported: String, Sequence, Dictionary, HashSet, Reference, and Custom
- No runtime dependencies. Library is compile time dependence only.
Usage
Add package
Add the nuget package to your projects.
dotnet add package Equatable.Generator
Prevent including Equatable.Generator as a dependency
<PackageReference Include="Equatable.Generator" PrivateAssets="all" />
Requirements
This library requires:
- Target framework .NET Standard 2.0 or greater
- Project C#
LangVersion
8.0 or higher
Equatable Attributes
Place [Equatable]
attribute on a class
, record
or struct
. The source generator will create a partial with overrides for Equals
and GetHashCode
for all public properties.
[Equatable]
Marks the class to generate overrides forEquals
andGetHashCode
The default comparer used in the implementation of
Equals
andGetHashCode
isEqualityComparer<T>.Default
. Customize the comparer used with the following attributes.[IgnoreEquality]
Ignore property inEquals
andGetHashCode
implementations[StringEquality]
Use specifiedStringComparer
when comparing strings[SequenceEquality]
UseEnumerable.SequenceEqual
to determine whether enumerables are equal[DictionaryEquality]
Use to determine if dictionaries are equal[HashSetEquality]
UseISet<T>.SetEquals
to determine whether enumerables are equal[ReferenceEquality]
UseObject.ReferenceEquals
to determines whether instances are the same instance[EqualityComparer]
Use the specifiedEqualityComparer
Example Usage
Example of using the attributes to customize the source generation of Equals
and GetHashCode
[Equatable]
public partial class UserImport
{
[StringEquality(StringComparison.OrdinalIgnoreCase)]
public string EmailAddress { get; set; } = null!;
public string? DisplayName { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public DateTimeOffset? LastLogin { get; set; }
[IgnoreEquality]
public string FullName => $"{FirstName} {LastName}";
[HashSetEquality]
public HashSet<string>? Roles { get; set; }
[DictionaryEquality]
public Dictionary<string, int>? Permissions { get; set; }
[SequenceEquality]
public List<DateTimeOffset>? History { get; set; }
}
About
Generating Equals from properties
How to use
Example ( source csproj, source files )
- CSharp Project
- Program.cs
- Person.cs
This is the CSharp Project that references Equatable.Generator
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Equatable.Generator" Version="2.0.0" />
</ItemGroup>
</Project>
This is the use of Equatable.Generator in Program.cs
// See https://aka.ms/new-console-template for more information
using GeneratorEqualsDemo;
var p1 = new Person()
{
ID = 1,
FirstName = "Andrei",
LastName = "Ignat"
};
var p2= new Person()
{
ID = 2,
FirstName = "aNdrei",
LastName = "Ignat"
};
Console.WriteLine(p1==p2);
This is the use of Equatable.Generator in Person.cs
using Equatable.Attributes;
namespace GeneratorEqualsDemo;
[Equatable]
partial class Person
{
[IgnoreEquality]
public int ID { get; set; }
[StringEquality(StringComparison.OrdinalIgnoreCase)]
public string? FirstName { get; set; }
[StringEquality(StringComparison.OrdinalIgnoreCase)]
public string? LastName { get; set; }
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- GeneratorEqualsDemo.Person.Equatable.g.cs
- GeneratorEqualsDemo.Person.Generator.Equals.g.cs
// <auto-generated />
#nullable enable
namespace GeneratorEqualsDemo
{
partial class Person : global::System.IEquatable<global::GeneratorEqualsDemo.Person?>
{
/// <inheritdoc />
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Equatable.SourceGenerator", "2.0.0+10ad4b045a688eb10980afcd11ddb8e64505eda6")]
public bool Equals(global::GeneratorEqualsDemo.Person? other)
{
return !(other is null)
&& global::System.StringComparer.OrdinalIgnoreCase.Equals(FirstName, other.FirstName)
&& global::System.StringComparer.OrdinalIgnoreCase.Equals(LastName, other.LastName);
}
/// <inheritdoc />
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Equatable.SourceGenerator", "2.0.0+10ad4b045a688eb10980afcd11ddb8e64505eda6")]
public override bool Equals(object? obj)
{
return Equals(obj as global::GeneratorEqualsDemo.Person);
}
/// <inheritdoc />
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Equatable.SourceGenerator", "2.0.0+10ad4b045a688eb10980afcd11ddb8e64505eda6")]
public static bool operator ==(global::GeneratorEqualsDemo.Person? left, global::GeneratorEqualsDemo.Person? right)
{
return global::System.Collections.Generic.EqualityComparer<global::GeneratorEqualsDemo.Person?>.Default.Equals(left, right);
}
/// <inheritdoc />
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Equatable.SourceGenerator", "2.0.0+10ad4b045a688eb10980afcd11ddb8e64505eda6")]
public static bool operator !=(global::GeneratorEqualsDemo.Person? left, global::GeneratorEqualsDemo.Person? right)
{
return !(left == right);
}
/// <inheritdoc />
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Equatable.SourceGenerator", "2.0.0+10ad4b045a688eb10980afcd11ddb8e64505eda6")]
public override int GetHashCode(){
int hashCode = 1938039292;
hashCode = (hashCode * -1521134295) + global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(FirstName!);
hashCode = (hashCode * -1521134295) + global::System.StringComparer.OrdinalIgnoreCase.GetHashCode(LastName!);
return hashCode;
}
}
}
#nullable enable
#pragma warning disable CS0612,CS0618
#pragma warning disable CS0436
namespace GeneratorEqualsDemo
{
partial class Person : global::System.IEquatable<Person>
{
/// <summary>
/// Indicates whether the object on the left is equal to the object on the right.
/// </summary>
/// <param name="left">The left object</param>
/// <param name="right">The right object</param>
/// <returns>true if the objects are equal; otherwise, false.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
public static bool operator ==(
global::GeneratorEqualsDemo.Person? left,
global::GeneratorEqualsDemo.Person? right) =>
global::Generator.Equals.DefaultEqualityComparer<global::GeneratorEqualsDemo.Person?>.Default
.Equals(left, right);
/// <summary>
/// Indicates whether the object on the left is not equal to the object on the right.
/// </summary>
/// <param name="left">The left object</param>
/// <param name="right">The right object</param>
/// <returns>true if the objects are not equal; otherwise, false.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
public static bool operator !=(global::GeneratorEqualsDemo.Person? left, global::GeneratorEqualsDemo.Person? right) =>
!(left == right);
/// <inheritdoc/>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
public override bool Equals(object? obj) =>
Equals(obj as global::GeneratorEqualsDemo.Person);
/// <inheritdoc/>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
bool global::System.IEquatable<global::GeneratorEqualsDemo.Person>.Equals(global::GeneratorEqualsDemo.Person? obj) => Equals((object?) obj);
/// <inheritdoc/>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
protected bool Equals(global::GeneratorEqualsDemo.Person? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.GetType() == this.GetType()
&& global::Generator.Equals.DefaultEqualityComparer<global::System.String?>.Default.Equals(this.FirstName!, other.FirstName!)
&& global::Generator.Equals.DefaultEqualityComparer<global::System.String?>.Default.Equals(this.LastName!, other.LastName!)
;
}
/// <inheritdoc/>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Generator.Equals", "1.0.0.0")]
public override int GetHashCode()
{
var hashCode = new global::System.HashCode();
hashCode.Add(this.GetType());
hashCode.Add(
this.FirstName!,
global::Generator.Equals.DefaultEqualityComparer<global::System.String?>.Default);
hashCode.Add(
this.LastName!,
global::Generator.Equals.DefaultEqualityComparer<global::System.String?>.Default);
return hashCode.ToHashCode();
}
}
}
Usefull
Download Example (.NET C# )
Share Equatable.Generator
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Equatable.Generator