• Stars
    star
    111
  • Rank 314,510 (Top 7 %)
  • Language
    C#
  • License
    MIT License
  • Created about 11 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Generate Equals, GetHashCode and operators methods from properties.

Equals

Chat on Gitter NuGet Status

Generate Equals, GetHashCode and operator methods from properties for classes decorated with an [Equals] Attribute

This is an add-in for Fody

It is expected that all developers using Fody either become a Patron on OpenCollective, or have a Tidelift Subscription. See Licensing/Patron FAQ for more information.

Usage

See also Fody usage.

NuGet installation

Install the Equals.Fody NuGet package and update the Fody NuGet package:

PM> Install-Package Fody
PM> Install-Package Equals.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Add to FodyWeavers.xml

Add <Equals/> to FodyWeavers.xml

<Weavers>
  <Equals/>
</Weavers>

Your Code

[Equals]
public class Point
{
    public int X { get; set; }
    
    public int Y { get; set; }
    
    [IgnoreDuringEquals]
    public int Z { get; set; }
    
    [CustomEqualsInternal]
    bool CustomLogic(Point other)
    {
        return Z == other.Z || Z == 0 || other.Z == 0;
    }
    
    public static bool operator ==(Point left, Point right) => Operator.Weave(left, right);
    public static bool operator !=(Point left, Point right) => Operator.Weave(left, right);
}

[Equals]
public class CustomGetHashCode
{
    public int X { get; set; }

    [IgnoreDuringEquals]
    public int Z { get; set; }

    [CustomGetHashCode]
    int CustomGetHashCodeMethod()
    {
        return 42;
    }
    
    public static bool operator ==(CustomGetHashCode left, CustomGetHashCode right) => Operator.Weave(left, right);
    public static bool operator !=(CustomGetHashCode left, CustomGetHashCode right) => Operator.Weave(left, right);
}

Note:

  • unless you specify [Equals(DoNotAddEqualityOperators = true)], you must always add the == and != method stubs with the Operator.Weave() implementation (if you want to know why, see #10).
  • adding the == and != operators will result in compiler warnings CS0660 and CS0661, which tell you to implement custom Equals and GetHashCode implementations. Equals.Fody is doing this for you, but after the compiler runs. To suppress the false-positives you can either do so
    • per project, by adding <PropertyGroup><NoWarn>CS0660;CS0661</NoWarn></PropertyGroup> to the project file
    • per source file, by adding #pragma warning disable CS0660, CS0661.
  • implementing a custom hash code method (marked by [CustomGetHashCode]) is optional.

What gets compiled

public class Point : IEquatable<Point>
{
    public int X { get; set; }

    public int Y { get; set; }

    public int Z { get; set; }
    
    bool CustomLogic(Point other)
    {
        return Z == other.Z || Z == 0 || other.Z == 0;
    }

    public static bool operator ==(Point left, Point right)
    {
        return object.Equals((object)left, (object)right);
    }

    public static bool operator !=(Point left, Point right)
    {
        return !object.Equals((object)left, (object)right);
    }

    static bool EqualsInternal(Point left, Point right)
    {
        return left.X == right.X && left.Y == right.Y && leftt.CustomLogic(right);
    }

    public virtual bool Equals(Point right)
    {
        return !object.ReferenceEquals((object)null, (object)right) && (object.ReferenceEquals((object)this, (object)right) || Point.EqualsInternal(this, right));
    }

    public override bool Equals(object right)
    {
        return !object.ReferenceEquals((object)null, right) && (object.ReferenceEquals((object)this, right) || this.GetType() == right.GetType() && Point.EqualsInternal(this, (Point)right));
    }

    public override int GetHashCode()
    {
        return unchecked(this.X.GetHashCode() * 397 ^ this.Y.GetHashCode());
    }
}

public class CustomGetHashCode : IEquatable<CustomGetHashCode>
{
    public int X { get; set; }

    public int Z { get; set; }

    int CustomGetHashCodeMethod()
    {
        return 42;
    }

    static bool EqualsInternal(CustomGetHashCode left, CustomGetHashCode right)
    {
        return left.X == right.X;
    }

    public override bool Equals(CustomGetHashCode other)
    {
        return !object.ReferenceEquals(null, other) && (object.ReferenceEquals(this, other) || CustomGetHashCode.EqualsInternal(this, other));
    }

    public override bool Equals(object obj)
    {
        return !object.ReferenceEquals(null, obj) && (object.ReferenceEquals(this, obj) || (base.GetType() == obj.GetType() && CustomGetHashCode.EqualsInternal(this, (CustomGetHashCode)obj)));
    }

    public override int GetHashCode()
    {
        return (this.X.GetHashCode() * 397) ^ this.CustomGetHashCodeMethod();
    }

    public static bool operator ==(CustomGetHashCode left, CustomGetHashCode right)
    {
        return object.Equals(left, right);
    }

    public static bool operator !=(CustomGetHashCode left, CustomGetHashCode right)
    {
        return !object.Equals(left, right);
    }
}

Configurability

Through properties on the [Equals] attribute the following options can be set:

  • DoNotAddEqualityOperators => do not weave == and != operators
  • DoNotAddGetHashCode => do not override the int GetHashCode() methods
  • DoNotAddEquals => do not override the bool Equals(object other) method, do not add and implement IEquatable<T>
  • IgnoreBaseClassProperties => equality and hash code do not consider properties of base classes.
  • TypeCheck can be used to affect the equality logic.
    • ExactlyTheSameTypeAsThis (default): only equal, when the other object is of the same type as this. Imagine we have a class Foo with [Equals] and we have a sub-class Bar : Foo:
      • Foo may equal Foo
      • Bar may equal Bar
      • but Foo may never equal Bar
    • ExactlyOfType: only equal, when the other object is of the same as the method is added to. Consider a class Foo with [Equals(TypeCheck = ExactlyOfType)] and a sub-class Bar : Foo:
      • Foo may equal Foo
      • Bar may never equal Bar
      • Foo may never equal Bar
    • EqualsOrSubtype: equal, when the other object is of same type as the method is added to, or is of a sub type.

Icon

Icon courtesy of The Noun Project

More Repositories

1

Fody

Extensible tool for weaving .net assemblies
C#
4,325
star
2

Costura

Embed references as resources
C#
2,383
star
3

PropertyChanged

Injects INotifyPropertyChanged code into properties at compile time
C#
1,885
star
4

NullGuard

Adds null argument checks to an assembly
C#
686
star
5

MethodTimer

Injects some very basic method timing code.
C#
686
star
6

Home

The landing page for Fody repositories
C#
671
star
7

ConfigureAwait

Configure async code's ConfigureAwait at a global level
C#
444
star
8

MethodDecorator

Compile time decorator pattern via IL rewriting
C#
380
star
9

Anotar

Simplifies logging through a static class and some IL manipulation.
C#
262
star
10

AsyncErrorHandler

An extension for Fody to integrate error handling into async and TPL code
C#
104
star
11

FodyAddinSamples

A working sample for each Fody Addin
C#
95
star
12

ToString

Generate ToString method from public properties.
C#
82
star
13

Janitor

Simplifies the implementation of IDisposable
C#
76
star
14

Ionad

Replaces static method calls.
C#
68
star
15

InfoOf

Provides methodof, propertyof and fieldof equivalents of typeof .
C#
65
star
16

Virtuosity

Change all members to virtual as part of your build.
C#
61
star
17

PropertyChanging

Injects INotifyPropertyChanging code into properties at compile time.
C#
54
star
18

ExtraConstraints

Facilitates adding constraints for Enum and Delegate to types and methods.
C#
47
star
19

Visualize

Adds debugger attributes to help visualize objects.
C#
43
star
20

Caseless

Change string comparisons to be case insensitive.
C#
40
star
21

Resourcer

Simplifies reading embedded resources from an Assembly.
C#
37
star
22

LoadAssembliesOnStartup

Loads all the references on startup by actually using the types in the module initializer.
C#
36
star
23

Obsolete

Helps keep usages of ObsoleteAttribute consistent.
C#
32
star
24

EmptyConstructor

Adds an empty constructor to classes even if you have a non-empty one defined.
C#
29
star
25

Scalpel

Strips all testing code from an assembly
C#
27
star
26

AssertMessage

Add 'message' parameter to Assertions. Nunit, Mstest, Xunit is supported.
C#
22
star
27

Publicize

Converts non-public members to public hidden members
C#
12
star
28

UniversalAppSample

C#
7
star
29

.github

1
star