This is an example about what I´m trying to do:
public class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
Type type1 = this.GetType();
Type type2 = other.GetType();
if (type1 != type2)
return false;
if (type1 == typeof(A))
{
A a = (A)this;
A b = (A)other;
return a.Equals(b);
}
else if (type1 == typeof(B))
{
B c = (B)this;
B d = (B)other;
return c.Equals(d);
}
else
{
throw new Exception("Something is wrong");
}
}
}
public class A : Foo, IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public bool Equals(A other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2;
}
}
public class B : Foo, IEquatable<B>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public bool Equals(B other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
}
}
But as you can see above, I'd have to use many conditionals 'if' to identify the real type. The problem is I have to use the base class. For example:
A a = new A();
Foo foo = a;
foo.Equals(another);
As a direct answer your question, you appear to implement IEquatable<Foo> by always deferring to the (concrete) sub-class's IEquatable<self> implementation. This would look something like:
(Bad code, for demonstration only)
// You need to specify what you want when this method is called on a
// vanilla Foo object. I assume here that Foo is abstract. If not, please
// specify desired behaviour.
public bool Equals(Foo other)
{
if (other == null || other.GetType() != GetType())
return false;
// You can cache this MethodInfo..
var equalsMethod = typeof(IEquatable<>).MakeGenericType(GetType())
.GetMethod("Equals");
return (bool)equalsMethod.Invoke(this, new object[] { other });
}
But it really isn't clear why you need the equality comparisons to always go "through" the base-class's IEquatable<self> implementation.
The framework already has the virtual Equals method that will result in dispatching equality-calls to the appropriate method. In addition, EqualityComparar<T>.Default (which is used by most collection-types for making equality checks) already has the smarts to choose IEquatable<self>.Equals(self) or object.Equals(object)as appropriate.
Trying to create an implementation of equality in the base-class that just forwards the request is adding no value to anything, as far as I can see.
Without further explanation on why you need the base-class IEquatable<> implementation, I recommend just implementing equality properly on each type. For example:
public class A : Foo, IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public bool Equals(A other)
{
return other != null
&& Number1 == other.Number1
&& Number2 == other.Number2;
}
public override bool Equals(object obj)
{
return Equals(obj as A);
}
public override int GetHashCode()
{
return Number1 ^ Number2;
}
}
Try this piece of code:
public class Foo : IEquatable<Foo>
{
public virtual bool Equals(Foo other)
{
return true;
}
}
public class A : Foo,IEquatable<A>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public override bool Equals(Foo other)
{
if (other.GetType() == typeof(A))
{
return Equals((A)other);
}
throw new InvalidOperationException("Object is not of type A");
}
public bool Equals(A other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2;
}
}
public class B : Foo,IEquatable<B>
{
public int Number1 { get; set; }
public int Number2 { get; set; }
public int Number3 { get; set; }
public override bool Equals(Foo other)
{
if (other.GetType() == typeof(B))
{
return Equals((B)other);
}
throw new InvalidOperationException("Object is not of type B");
}
public bool Equals(B other)
{
return this.Number1 == other.Number1 && this.Number2 == other.Number2 && this.Number3 == other.Number3;
}
}
Note : You can use Assert functionality to do typechecking.
One option is to move the Number1 and Number2 properties to the base class, and only compare the member added to the subclass in the subclasses' equality methods.
class Foo
{
// move the common properties to the base class
public int Number1 { get; set; }
public int Number2 { get; set; }
public override bool Equals(object obj)
{
Foo objfoo = obj as Foo;
return
objfoo != null
// require objects being compared to be of
// the same derived type (optionally)
&& this.GetType() == obj.GetType()
&& objfoo.Number1 == this.Number1
&& objfoo.Number2 == this.Number2;
}
public override int GetHashCode()
{
// xor the hash codes of the elements used to evaluate
// equality
return Number1.GetHashCode() ^ Number2.GetHashCode();
}
}
class A : Foo, IEquatable<A>
{
// A has no properties Foo does not. Simply implement
// IEquatable<A>
public bool Equals(A other)
{
return this.Equals(other);
}
// can optionally override Equals(object) and GetHashCode()
// to call base methods here
}
class B : Foo, IEquatable<B>
{
// Add property Number3 to B
public int Number3 { get; set; }
public bool Equals(B other)
{
// base.Equals(other) evaluates Number1 and Number2
return base.Equals(other)
&& this.Number3 == other.Number3;
}
public override int GetHashCode()
{
// include Number3 in the hashcode, since it is used
// to evaluate equality
return base.GetHashCode() ^ Number3.GetHashCode();
}
public override bool Equals(object obj)
{
return this.Equals(obj as B);
}
}
I think that derived classes should not be handled in base classes. Usually, "Foo" will know nothing about A and B.
It's still possible to make the base IEquatable implementation virtual, allowing A and B to override it and perform their specific equality checks, even if both equality checking and checked instance are available only as "Foo" or "Object".
That would treat .Equals(Foo obj) like a more specific form of Object.Equals(Object obj).
Related
I have a MyCustomSet class with IEquatable implemented as shown below.
This works fantastic when I want to check equality for all three sets (SetA*, SetB*, and SetC*). But requirements dictate that I also need the ability to check equality for only SetA*, SetB*, or SetC*, or combination thereof (SetA* and SetB*, SetA* and SetC*, or SetB* and SetC*) and ignore checking equality for any other Set that is not required in the check.
Currently, I'm using foreach and LINQ to iterate through the Sets to perform partial equality checks, and that works, but that doesn't seem very efficient for large datasets.
Maybe the answer is looking at me straight in the face, but I don't see it because I don't have any idea how implement IEquatable that can handle different equality checks.
Would someone assist me with some suggestions or directions as to how this may be implemented? An example would be even more appreciated.
public static class HashCode
{
public const int Start = 17;
public static int Hash<T>(this int hash, T obj)
{
var h = EqualityComparer<T>.Default.GetHashCode(obj);
return unchecked((hash * 439) + h);
}
}
public class MyCustomSetModel
{
public sealed class MyCustomSet : IEquatable<MyCustomSet>
{
public string ItemName { get; set; }
public string ItemType { get; set; }
public double SetA1 { get; set; }
public double SetA2 { get; set; }
public double SetA3 { get; set; }
public double SetB1 { get; set; }
public double SetB2 { get; set; }
public double SetB3 { get; set; }
public double SetC1 { get; set; }
public double SetC2 { get; set; }
public double SetC3 { get; set; }
public bool Equals(MyCustomSet other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(other, this))
{
return true;
}
return
(
(this.ItemName == other.ItemName) &&
(this.ItemType == other.ItemType) &&
(this.SetA1 == other.SetA1) &&
(this.SetA2 == other.SetA2) &&
(this.SetA3 == other.SetA3) &&
(this.SetB1 == other.SetB1) &&
(this.SetB2 == other.SetB2) &&
(this.SetB3 == other.SetB3) &&
(this.SetC1 == other.SetC1) &&
(this.SetC2 == other.SetC2) &&
(this.SetC3 == other.SetC3)
);
}
public override bool Equals(object obj) => Equals(obj as MyCustomSet);
public override int GetHashCode()
{
unchecked
{
return HashCode.Start
.Hash(ItemName)
.Hash(ItemType)
.Hash(SetA1)
.Hash(SetA2)
.Hash(SetA3)
.Hash(SetB1)
.Hash(SetB2)
.Hash(SetB3)
.Hash(SetC1)
.Hash(SetC2)
.Hash(SetC3);
}
}
}
}
* Update: *
Thanks to Matthew Watson who pointed me in the right direction. So I implemented a custom comparer as follows. It looks like it is working, but if anyone sees potential problems or room for better implementation, please feel free to comment.
public sealed class MyCustomSetComparer : IEqualityComparer<MyCustomSet>
{
private bool _compareSetA;
private bool _compareSetB;
private bool _compareSetC;
public MyCustomSetComparer(bool compareSetA = true, bool compareSetB = true, bool compareSetC = true)
{
_compareSetA = compareSetA;
_compareSetB = compareSetB;
_compareSetC = compareSetC;
}
public bool Equals(MyCustomSet x, MyCustomSet y)
{
if (Object.ReferenceEquals(x, y))
{
return true;
}
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
{
return false;
}
bool result =
(x.ItemName == y.ItemName) &&
(x.ItemType == y.ItemType);
if (_compareSetA)
{
result = result &&
(x.SetA1 == y.SetA1) &&
(x.SetA2 == y.SetA2) &&
(x.SetA3 == y.SetA3);
}
if (_compareSetB)
{
result = result &&
(x.SetB1 == y.SetB1) &&
(x.SetB2 == y.SetB2) &&
(x.SetB3 == y.SetB3);
}
if (_compareSetC)
{
result = result &&
(x.SetC1 == y.SetC1) &&
(x.SetC2 == y.SetC2) &&
(x.SetC3 == y.SetC3);
}
return result;
}
public int GetHashCode(MyCustomSet item)
{
if (Object.ReferenceEquals(item, null))
{
return 0;
}
int hash = HashCode.Start
.Hash(item.ItemName)
.Hash(item.ItemType);
if (_compareSetA)
{
hash = hash.Hash(item.SetA1)
.Hash(item.SetA2)
.Hash(item.SetA3);
}
if (_compareSetB)
{
hash = hash.Hash(item.SetB1)
.Hash(item.SetB2)
.Hash(item.SetB3);
}
if (_compareSetC)
{
hash = hash.Hash(item.SetC1)
.Hash(item.SetC2)
.Hash(item.SetC3);
}
unchecked
{
return hash;
}
}
}
As per Matthew Watson's comment, this is a perfect time to use IEqualityComparer<T> rather than IEquatable<T>. I would suggest only implementing IEquatable<T> when there's a single obvious, natural definition of equality for a type. When there are multiple options for equality and no single option is more reasonable than the others, implement IEqualityComparer<T> - either in several different implementation types, or in a single implementation type that is parameterized in terms of creation.
I have class A:
public class A : IEquatable<A>
{
public B Owner { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as A);
}
public bool Equals([AllowNull] A other)
{
return other is A a &&
EqualityComparer<B>.Default.Equals(Owner, a.Owner);
}
}
And I have a class B:
public class B : IEquatable<B>
{
public List<A> Children { get; set; } = new List<A>();
public override bool Equals(object obj)
{
return Equals(obj as B);
}
public bool Equals([AllowNull] B other)
{
return other is B b &&
EqualityComparer<List<A>>.Default.Equals(Children, b.Children);
}
}
The problem I am having is making Equals() methods of the above classes work. The Equals() methods in the example are generated by VS Code, but always return false in case of class B.
I also tried using LINQ expressions (such as SequenceEqual method), but it always results in Stack Overflow (because of circular dependency?).
As a side note, I used .NET Core 3.0 to run this.
So, I managed to find the answer to my question. I just implemented my own custom IEqualityComparer. (in the example below I added public Guid ID property to both classes to do proper GetHashCode()).
public class BComparer : IEqualityComparer<B>
{
public bool Equals([AllowNull] B x, [AllowNull] B y)
{
if (x is null || y is null) {return false;}
if (x.ID == y.ID) {
return x.Children.SequenceEqual(y.Children);
} else {
return false;
}
}
public int GetHashCode([DisallowNull] B obj)
{
return obj.ID.ToString().GetHashCode();
}
}
I have a class with ten properties, and am looking for objects that have the same values in these properties except for two specific properties.
I'm thinking of extending a base class which has the eight properties that I want to compare, and then extend this base class, calling the base Equals method?
What would be the least code-intensive way of determining this?
Whenever you wish to compare two instances of a custom class for value equality (that is two objects with the same value or values) rather than reference quality (that two object references refer to the same underlying object), you must take this into account in the design of the object. There is a pattern you can follow to do this. In a nutshell, it involves implementing the System.IEquatable<T> interface, which defines a method with the signature bool Equals(MyClass other). You implement this method to return true when other has the same 'value' is this object. Here is a basic example ofsimple object that has 4 properties that determine its value equality:
class MyClass : IEquatable<MyClass>
{
public int ImportantProperty1 { get; set; }
public int ImportantProperty2 { get; set; }
public int ImportantProperty3 { get; set; }
public int ImportantProperty4 { get; set; }
public int NonImportantProperty { get; set; }
public bool Equals(MyClass other)
{
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
}
With the above code you will be able to do the following:
MyClass a = new MyClass() { };
MyClass b = new MyClass() { };
if (a.Equals(b))
Console.WriteLine("a and b are equal");
This is the bare minimum. However, as noted in the linked article, you might want to consider the following optimizations:
Override the virtual Object.Equals(Object) method so that calls the type specific Equals method. This will allow you to compare MyClass with objects of other types:
public override bool Equals(object obj)
{
return this.Equals(obj as MyClass);
}
Add to bool Equals(MyClass) method a check to see if other references the same object as this:
public bool Equals(MyClass other)
{
if (Object.ReferenceEquals(this, other))
return true;
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
Override Object.GetHashCode() method so that two objects that have value equality produce the same hash code. This is the pattern I use when implement this method in this kind of scenario:
public override int GetHashCode()
{
unchecked {
int hash = 17;
hash = hash * 23 + ImportantProperty1.GetHashCode();
hash = hash * 23 + ImportantProperty2.GetHashCode();
hash = hash * 23 + ImportantProperty3.GetHashCode();
hash = hash * 23 + ImportantProperty4.GetHashCode();
return hash;
}
}
Optionally override the == and != operators. Unless these are overridden they will default reference equality. See the linked article for an example.
Here's my example in full:
namespace ValueEquality
{
class MyClass : IEquatable<MyClass>
{
public int ImportantProperty1 { get; set; }
public int ImportantProperty2 { get; set; }
public int ImportantProperty3 { get; set; }
public int ImportantProperty4 { get; set; }
public int NonImportantProperty { get; set; }
public bool Equals(MyClass other)
{
if (Object.ReferenceEquals(this, other))
return true;
return
(!Object.ReferenceEquals(this, null)) &&
(this.ImportantProperty1 == other.ImportantProperty1) &&
(this.ImportantProperty2 == other.ImportantProperty2) &&
(this.ImportantProperty3 == other.ImportantProperty3) &&
(this.ImportantProperty4 == other.ImportantProperty4);
}
public override bool Equals(object obj)
{
return this.Equals(obj as MyClass);
}
public override int GetHashCode()
{
unchecked {
int hash = 17;
hash = hash * 23 + ImportantProperty1.GetHashCode();
hash = hash * 23 + ImportantProperty2.GetHashCode();
hash = hash * 23 + ImportantProperty3.GetHashCode();
hash = hash * 23 + ImportantProperty4.GetHashCode();
return hash;
}
}
}
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass() { };
MyClass b = new MyClass() { };
if (a.Equals(b))
Console.WriteLine("a and b are equal");
}
}
}
What would be the best (most elegant or performing) way of overloading the equality operator on a class containing only string attributes?
Example:
class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
}
I know how to overload the operator itself, however, I am wondering about the following points:
Is there a way to elegantly compare such two objects (e.g. without having to write an if statement containing mutual comparisons of all the attributes
What would be a good implementation of the GetHashCode() method in such case
How about something like this, Just create array of all properties and a loop.
internal class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
private string[] AllProperties//Array of all properties
{
get
{
return new[]
{
FirstAttribute,
SecondAttribute,
ThirdAttribute,
FourthAttribute,
FifthAttribute
};
}
}
protected bool Equals(MagicClass other)
{
var thisProps = this.AllProperties;
var otherProps = other.AllProperties;
return thisProps.SequenceEqual(otherProps);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MagicClass) obj);
}
public override int GetHashCode()
{
unchecked
{
var thisProps = this.AllProperties;
int hashCode = 0;
foreach (var prop in thisProps)
{
hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
}
return hashCode;
}
}
}
Then you can call Equals method inside your operator overload. If you're lazy to create AllProperties array you can use Reflection but IMO reflection is overkill here.
Not saying this is the 'best' or the most elegant solution, but I'd have the tendency to use an array and an index initializer, using an enumeration, so I could reuse get and set logic and in this case reset a hash code for a quick first comparison.
The advantage of the enumeration is, that you don't have to recheck your compare logic when an attribute is added, and you can prevent the overhead of resorting to reflection.
class MagicClass
{
string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];
public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
{
get
{
return Values[(int)Value];
}
set
{
Values[(int)Value] = value;
hash = null;
}
}
int? hash; //buffered for optimal dictionary performance and == comparisson
public override int GetHashCode()
{
if (hash == null)
unchecked
{
hash = Values.Sum(s => s.GetHashCode());
}
return hash.Value;
}
public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
{
if(ReferenceEquals(v1,v2))return true;
if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
return v1.Values.SequenceEqual(v2.Values);
}
public static bool operator !=(MagicClass v1, MagicClass v2)
{
return !(v1 == v2);
}
//optional, use hard named properties as well
public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}
public enum MagicClassValues
{
FirstAttribute,
SecondAttribute,
//etc
}
This question already has answers here:
What's the best strategy for Equals and GetHashCode?
(7 answers)
Closed 9 years ago.
I have never really done this before so i was hoping that someone could show me the correct what of implementing a override of Except() and GetHashCode() for my class.
I'm trying to modify the class so that i can use the LINQ Except() method.
public class RecommendationDTO{public Guid RecommendationId { get; set; }
public Guid ProfileId { get; set; }
public Guid ReferenceId { get; set; }
public int TypeId { get; set; }
public IList<TagDTO> Tags { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
public bool IsActive { get; set; }
public object ReferencedObject { get; set; }
public bool IsSystemRecommendation { get; set; }
public int VisibilityScore { get; set; }
public RecommendationDTO()
{
}
public RecommendationDTO(Guid recommendationid,
Guid profileid,
Guid referenceid,
int typeid,
IList<TagDTO> tags,
DateTime createdon,
DateTime modifiedon,
bool isactive,
object referencedobject)
{
RecommendationId = recommendationid;
ProfileId = profileid;
ReferenceId = referenceid;
TypeId = typeid;
Tags = tags;
CreatedOn = createdon;
ModifiedOn = modifiedon;
ReferencedObject = referencedobject;
IsActive = isactive;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
RecommendationDTO p = obj as RecommendationDTO;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (ReferenceId == p.ReferenceId);// && (y == p.y);
}
public bool Equals(RecommendationDTO p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (ReferenceId == p.ReferenceId);// && (y == p.y);
}
//public override int GetHashCode()
//{
// return ReferenceId;// ^ y;
//}}
I have taken a look at http://msdn.microsoft.com/en-us/library/ms173147.aspx but i was hoping someone could show me within my own example.
Any help would be appreciated.
Thank you
You can override Equals() and GetHashCode() on your class like this:
public override bool Equals(object obj)
{
var item = obj as RecommendationDTO;
if (item == null)
{
return false;
}
return this.RecommendationId.Equals(item.RecommendationId);
}
public override int GetHashCode()
{
return this.RecommendationId.GetHashCode();
}
public override bool Equals(System.Object obj)
{
// Check if the object is a RecommendationDTO.
// The initial null check is unnecessary as the cast will result in null
// if obj is null to start with.
var recommendationDTO = obj as RecommendationDTO;
if (recommendationDTO == null)
{
// If it is null then it is not equal to this instance.
return false;
}
// Instances are considered equal if the ReferenceId matches.
return this.ReferenceId == recommendationDTO.ReferenceId;
}
public override int GetHashCode()
{
// Returning the hashcode of the Guid used for the reference id will be
// sufficient and would only cause a problem if RecommendationDTO objects
// were stored in a non-generic hash set along side other guid instances
// which is very unlikely!
return this.ReferenceId.GetHashCode();
}
Be careful when using a primary key as your test for equality in overriding Equals() because it only works AFTER the object has been persisted. Prior to that your objects don't have primary keys yet and the IDs of the ones in memory are all zero.
I use base.Equals() if either of the object IDs is zero but there probably is a more robust way.