Make two classes that are dependent on each other equatable - c#

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();
}
}

Related

I get the same code when overriding C# Equals method. How should I collapse duplicate code?

I have 2 custom sealed classes: Line and ColoredLine. Both implement the IEquals<T> interface. But when I am overriding IEquals method, I get this:
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj == this)
{
return true;
}
if (!(obj is ColoredLine))
{
return false;
}
return this.Equals((ColoredLine)obj);
}
and
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj == this)
{
return true;
}
if (!(obj is Line))
{
return false;
}
return this.Equals((Line)obj);
}
The difference in only a type. Should I collapse it?
I would stick to the MS guidelines
return Equals(obj as ColoredLine);
is short enough
I would let ColoredLine inherit from Line and also use Line.Equals to check if two colored-lines are equal, so like this:
public class Line: IEquatable<Line>
{
public int Length { get; set; }
public override bool Equals(object? obj)
{
return obj is Line l && this.Equals(l);
}
public bool Equals(Line? other)
{
return Length == other?.Length;
}
}
public class ColoredLine: Line, IEquatable<ColoredLine>
{
public System.Drawing.Color Color { get; set; }
public override bool Equals(object? obj)
{
return obj is ColoredLine cl && this.Equals(cl);
}
public bool Equals(ColoredLine? other)
{
return base.Equals(other) && Color == other?.Color;
}
}
Remember that you also should override GetHashCode whenever you override Equals.

Determining if two instances are the same except for two properties

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");
}
}
}

Global type aliases in C#

let me start right away with the code:
class Item {
public int highestBuyOffer;
public int lowestSellOffer;
[...]
}
I would like to prevent people using this class from accidently assigning a buy offer value to a sell offer value and the other way round (like someBuyOffer = someSellOffer). That's why I want to create my own types:
class Item {
public BuyOffer highestBuyOffer;
public SellOffer lowestSellOffer;
[...]
}
Creating a struct for it seems overkill, as these both of values should behave exactly like an int.
The using directive is not what I want because:
It is only valid for one file
It does not count as a type, it's just a synonym
I made this class to cover identical needs:
public class NamedInt : IComparable<int>, IEquatable<int>
{
internal int Value { get; }
protected NamedInt() { }
protected NamedInt(int val) { Value = val; }
protected NamedInt(string val) { Value = Convert.ToInt32(val); }
public static implicit operator int (NamedInt val) { return val.Value; }
public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
public static bool operator !=(NamedInt a, int b) { return !(a==b); }
public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }
public bool Equals(int other) { return Equals(new NamedInt(other)); }
public override bool Equals(object other) {
if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
return Equals(new NamedInt(other.ToString()));
}
private bool Equals(NamedInt other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(Value, other.Value);
}
public int CompareTo(int other) { return Value - other; }
public int CompareTo(NamedInt other) { return Value - other.Value; }
public override int GetHashCode() { return Value.GetHashCode(); }
public override string ToString() { return Value.ToString(); }
}
And to consume it in your case:
public class BuyOffer: NamedInt {
public BuyOffer(int value) : base(value) { }
public static implicit operator BuyOffer(int value) { return new BuyOffer(value); }
}
public class SellOffer: NamedInt {
public SellOffer(int value) : base(value) { }
public static implicit operator SellOffer(int value) { return new SellOffer(value); }
}
If you need to be able to serialize it (Newtonsoft.Json), let me know and I'll add the code.

Removed object property duplication from list

This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
and i have a list of my object:
List<Capture> list = db.Captures.Where(x => x.family == "Web").ToList();
What i want to do is get new list that removed the duplicate protocol.
for example if i have in my list 10 object and 9 of them with protocol DOC and 1 PDF i want a new list with only 2 object DOC and 1 PDF
There are several ways to do this, depending on how you generally want to use the instances of your MyObject class.
The easiest one is implementing the IEquatable<T> interface so as to compare only the protocol fields:
public class MyObject : IEquatable<MyObject>
{
public sealed override bool Equals(object other)
{
return Equals(other as MyObject);
}
public bool Equals(MyObject other)
{
if (other == null) {
return false;
} else {
return this.protocol == other.protocol;
}
}
public override int GetHashCode()
{
return protocol.GetHashCode();
}
}
You can then call Distinct before converting your enumerable into a list.
Alternatively, you can use the Distinct overload that takes an IEqualityComparer.
The equality comparer would have to be an object that determines equality based on your criteria, in the case described in the question, by looking at the protocol field:
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
if (x == null) {
return y == null;
} else {
if (y == null) {
return false;
} else {
return x.protocol == y.protocol;
}
}
}
public int GetHashCode(MyObject obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.protocol.GetHashCode();
}
}
I believe this is the simplest approach: The following will group list by protocol and then get the first instance from each group to produce an enumerable with one instance of each type of protocol.
list.GroupBy(x => protocol, x => x)
.SelectMany(k, v => v.First());
You could either use Distinct, or use the same solution provided here:
Distinct() with lambda?
Select distinct protocols, loop on them and subselect only first object of the same protocol - thus you'll get the list you need.

How can I improve this code: Inheritance and IEquatable<>

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).

Categories