GE

generator.equals

A source generator that creates Equals, GetHashCode, and equality operators for C# classes and structs.

Install

mkdir -p .claude/skills/generator-equals && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10956" && unzip -o skill.zip -d .claude/skills/generator-equals && rm skill.zip

Installs to .claude/skills/generator-equals

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Guidance for using Generator.Equals — a C# source generator for auto-generating Equals, GetHashCode, operators, and Diff/Inequalities methods via attributes.
157 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Auto-generates Equals and GetHashCode
  • Implements IEquatable and equality operators
  • Generates diff and inequality methods
  • Supports collection and precision-based equality

How it works

It uses C# source generators to implement equality logic at compile time, avoiding runtime reflection.

Inputs & outputs

You give it
C# class or record
You get back
Generated equality members

When to use generator.equals

  • Implementing value-based equality for DTOs
  • Adding precision-based numeric comparisons
  • Configuring collection equality behavior

About this skill

Generator.Equals

C# source generator that auto-implements IEquatable<T>, Equals(), GetHashCode(), ==/!= operators, and an Inequalities() diff method at compile time using attributes. No runtime reflection.

Setup

Add both packages:

<PackageReference Include="Generator.Equals" PrivateAssets="all" />
<PackageReference Include="Generator.Equals.Runtime" />

Requires C# 9.0+. The type must be partial.

Attributes Quick Reference

Type-level

AttributePurpose
[Equatable]Generates equality members for the type
[Equatable(Explicit = true)]Only compare properties with explicit equality attributes
[Equatable(IgnoreInheritedMembers = true)]Skip base class members entirely

Property/Field-level

AttributeUse When
[DefaultEquality]Default comparer. Required on fields (fields are excluded by default).
[IgnoreEquality]Skip this member
[OrderedEquality]Compare collection elements in order (like SequenceEqual)
[UnorderedEquality]Compare collection elements ignoring order
[SetEquality]Compare as sets (duplicates ignored)
[ReferenceEquality]Compare by reference only
[StringEquality(StringComparison.X)]String-specific comparison (string props only)
[PrecisionEquality(0.001)]Tolerance-based numeric comparison
[CustomEquality(typeof(MyComparer))]Custom IEqualityComparer<T>

Comparer constructor patterns (for collection and custom attributes)

[OrderedEquality]                                                          // Default comparer
[OrderedEquality(typeof(MyComparer))]                                     // Type with static Default member
[OrderedEquality(typeof(StringComparer), nameof(StringComparer.Ordinal))] // Named static member
[OrderedEquality(StringComparison.OrdinalIgnoreCase)]                     // StringComparison shorthand

Best Practices

Always annotate collection properties

// WRONG - produces diagnostic GE001
public List<int> Items { get; set; }

// RIGHT
[OrderedEquality]
public List<int> Items { get; set; }

Two exceptions: a collection type that is itself [Equatable] already compares structurally, so no attribute is needed. And [DefaultEquality] opts the member into the type's own Equals — use it for collections that implement their own value equality.

[DefaultEquality]
public MyImmutableList<int> Items { get; set; }   // MyImmutableList compares structurally itself

Use Explicit mode when only a few properties matter

[Equatable(Explicit = true)]
partial class User
{
    [DefaultEquality] public string Id { get; set; }     // Compared
    public string DisplayName { get; set; }               // Ignored
    public DateTime LastLogin { get; set; }               // Ignored
}

Mark fields explicitly

Fields are not included by default. You must annotate them:

[DefaultEquality]
private int _version;

Use the generated EqualityComparer

Every [Equatable] type gets a nested EqualityComparer class:

// Use in dictionaries, HashSets, LINQ
var dict = new Dictionary<MyType, string>(MyType.EqualityComparer.Default);
var distinct = items.Distinct(MyType.EqualityComparer.Default);

Use Inequalities for diff/audit

foreach (var diff in MyType.EqualityComparer.Default.Inequalities(oldObj, newObj))
    Console.WriteLine(diff);
// Output: Name: John -> Jane
// Output: Addresses["home"].Street: 123 Main St -> 456 Oak Ave

Nested [Equatable] objects in collections are auto-drilled — you get per-field diffs, not "entire object changed".

Common Pitfalls

  1. Non-partial type (GE006) — The type MUST be declared partial.

  2. Manual Equals/GetHashCode (GE005) — Do NOT override Equals() or GetHashCode() manually on an [Equatable] type. The generator owns these.

  3. Conflicting collection attributes (GE007) — Only ONE of [OrderedEquality], [UnorderedEquality], [SetEquality] per property.

  4. Hash code instability[UnorderedEquality], [SetEquality], and [PrecisionEquality] return hash code 0 or exclude from hashing. Types using these are poor dictionary keys.

  5. Inheritance with [Equatable] — The generator walks the full inheritance chain. If any ancestor has [Equatable] or overrides Equals(), it calls base.Equals(). Use IgnoreInheritedMembers = true to opt out.

  6. Overriding properties inherits attributes — A Child overriding a Parent's [OrderedEquality] virtual int[] Values automatically inherits [OrderedEquality]. Do NOT re-annotate unless you want to change behavior.

  7. [StringEquality] on non-string (GE008) — Only valid on string properties.

  8. [PrecisionEquality] types (GE010) — Only float, double, decimal, int, long, short, sbyte and their nullable variants.

Examples

Basic class

[Equatable]
partial class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    [IgnoreEquality]
    public DateTime LastUpdated { get; set; }
}

Record with collections

[Equatable]
partial record Customer(
    string Id,
    string Name,
    [property: OrderedEquality] ImmutableArray<Address> Addresses,
    [property: UnorderedEquality] ImmutableDictionary<string, string> Tags
);

Struct with custom comparer

[Equatable]
partial struct Coordinate
{
    [PrecisionEquality(0.0001)]
    public double Latitude { get; set; }

    [PrecisionEquality(0.0001)]
    public double Longitude { get; set; }
}

Inheritance

[Equatable]
partial class Animal
{
    public string Species { get; set; }
}

[Equatable]
partial class Pet : Animal
{
    public string Name { get; set; }
    // Species is also compared via base.Equals()
}

Diff / Inequalities

var diffs = Customer.EqualityComparer.Default.Inequalities(before, after);
foreach (var d in diffs)
{
    // d.Path  — e.g., "Addresses[0].Street"
    // d.Left  — old value
    // d.Right — new value
    Console.WriteLine(d);
}

Diagnostics

CodeDescription
GE001Collection missing equality attribute
GE002Complex property missing [Equatable]
GE003Collection element missing [Equatable]
GE005Manual Equals/GetHashCode with [Equatable]
GE006[Equatable] on non-partial type
GE007Conflicting equality attributes
GE008[StringEquality] on non-string
GE009Collection attribute on non-collection
GE010[PrecisionEquality] on unsupported type

All diagnostics have automatic code fixes.

When not to use it

  • When the type cannot be declared partial
  • When manual Equals/GetHashCode overrides are required

Prerequisites

Generator.Equals package

Limitations

  • Type must be partial
  • Conflicting collection attributes are not allowed

How it compares

It provides high-performance, compile-time equality generation instead of manual or reflection-based implementations.

Compared to similar skills

generator.equals side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
generator.equals (this skill)04moNo flagsIntermediate
csharp-developer432moNo flagsAdvanced
csharp-pro94moNo flagsIntermediate
update-roslyn-version22moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry