Awesome
<img src="/package_icon.png" height="30px"> Equals
Generate Equals, GetHashCode and operator methods from properties for classes decorated with an [Equals]
Attribute
See Milestones for release notes.
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 theOperator.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 customEquals
andGetHashCode
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
.
- per project, by adding
- 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!=
operatorsDoNotAddGetHashCode
=> do not override theint GetHashCode()
methodsDoNotAddEquals
=> do not override thebool Equals(object other)
method, do not add and implementIEquatable<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 asthis
. Imagine we have a classFoo
with[Equals]
and we have a sub-class Bar : Foo
:Foo
may equalFoo
Bar
may equalBar
- but
Foo
may never equalBar
ExactlyOfType
: only equal, when the other object is of the same as the method is added to. Consider a classFoo
with[Equals(TypeCheck = ExactlyOfType)]
and a sub-class Bar : Foo
:Foo
may equalFoo
Bar
may never equalBar
Foo
may never equalBar
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