I'm following the MSDN guidance for value equality, and I found a case that the documentation didn't cover, equality for a base class.
A little background:
I'm working on a Mahjong game (4-player, not matching), and I'm working on defining the tiles. Tiles can be broken into two groups: suits, which have a number associated with them (and can be put together in sequences, like 2-3-4) and honor tiles, which have no number.
Here's what I have so far:
public enum MahjongSuitType
{
Bamboo = 1,
Character,
Dot
}
public enum MahjongSuitNumber
{
One = 1,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine
}
public enum MahjongHonorType
{
GreenDragon = 1,
RedDragon,
WhiteDragon,
EastWind,
SouthWind,
WestWind,
NorthWind
}
public abstract class MahjongTile
{
}
public class MahjongSuitTile : MahjongTile, IEquatable<MahjongTile>
{
public MahjongSuitType SuitType { get; private set; }
public MahjongSuitNumber SuitNumber { get; private set; }
public bool IsRedBonus { get; private set; } //this has no bearing on equality
public MahjongSuitTile(MahjongSuitType suitType,
MahjongSuitNumber suitNumber,
bool isRedBonus = false)
{
this.SuitType = suitType;
this.SuitNumber = suitNumber;
this.IsRedBonus = isRedBonus;
}
public override bool Equals(object obj)
{
return this.Equals(obj as MahjongTile);
}
public bool Equals(MahjongTile other)
{
if (Object.ReferenceEquals(other, null))
return false;
if (Object.ReferenceEquals(other, this))
return true;
MahjongSuitTile otherSuitTile = other as MahjongSuitTile;
if (Object.ReferenceEquals(otherSuitTile, null))
return false;
return (this.SuitType == otherSuitTile.SuitType) &&
(this.SuitNumber == otherSuitTile.SuitNumber);
}
public override int GetHashCode()
{
return this.SuitType.GetHashCode() ^ this.SuitNumber.GetHashCode();
}
}
public class MahjongHonorTile : MahjongTile, IEquatable<MahjongTile>
{
public MahjongHonorType HonorType { get; private set; }
public MahjongHonorTile(MahjongHonorType honorType)
{
this.HonorType = HonorType;
}
public override bool Equals(object obj)
{
return this.Equals(obj as MahjongTile);
}
public bool Equals(MahjongTile other)
{
if (Object.ReferenceEquals(other, null))
return false;
if (Object.ReferenceEquals(other, this))
return true;
MahjongHonorTile otherHonorTile = other as MahjongHonorTile;
if (Object.ReferenceEquals(otherHonorTile, null))
return false;
return this.HonorType == otherHonorTile.HonorType;
}
public override int GetHashCode()
{
return this.HonorType.GetHashCode();
}
}
For the majority of the code, I'd like to refer to the tiles via the base class, something like:
List<MahjongTile> hand = new List<MahjongTile>() { ... };
HashSet<MahjongTile> dragonTiles = new HashSet()
{
new MahjongHonorTile(MahjongHonorType.GreenDragon),
new MahjongHonorTile(MahjongHonorType.RedDragon),
new MahjongHonorTile(MahjongHonorType.WhiteDragon)
}
IEnumerable<MahjongTile> dragonTilesInHand = hand.Where(t => dragonTiles.Contains(t));
My Question: how should I define equality in the MahjongTile base class?
Since Object.Equals is virtual, the subclasses implementation override the method. No further code is needed.
Related
I have several classes with id property of the same type int?:
public class Person {
public int? id { get; set; }
}
public class Project {
public int? id { get; set; }
}
// etc...
When writing code it happened that I compared semantically wrong types:
if (person.id == project.id), and of course there was no warning until I found the bug.
How could I create some kind of underlying type enforcement, or even better, a compiler warning, or something like that, that warns me not everything looks o.k.?
I can think of creating an Equals(Person p) { return p.id == this.id } but I'd prefer some other mechanism that could be used more 'freely'.
You need to override Equals and GetHashCode to be able to compare objects directly.
Try like this:
public sealed class Person : IEquatable<Person>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Person(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Person)
return Equals((Person)obj);
return false;
}
public bool Equals(Person obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Person left, Person right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Person left, Person right)
{
return !(left == right);
}
}
public sealed class Project : IEquatable<Project>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Project(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Project)
return Equals((Project)obj);
return false;
}
public bool Equals(Project obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Project left, Project right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Project left, Project right)
{
return !(left == right);
}
}
I also implemented IEquatable<Person> and == and != for good measure.
Now you can write person1 == this if this is a Person, but you would have a compiler error if this were a Project.
This is what tests are for. This is why you should write tests. Tests should pick up on these kind of errors.
But if you really want to go overkill, create a custom struct to store your IDs:
public struct Id<T> {
public int? ID { get; }
public static implicit operator Id<T>(int id) {
return new Id<T>(id);
}
public Id(int? id) { ID = id; }
public static bool operator ==(Id<T> lhs, Id<T> rhs) {
return lhs.ID == rhs.ID;
}
public static bool operator !=(Id<T> lhs, Id<T> rhs) {
return lhs.ID != rhs.ID;
}
}
// usage:
public class Person {
public Id<Person> Id { get; set; }
}
public class Project {
public Id<Project> Id { get; set; }
}
Whenever you try to compare Person.Id with Project.Id, the compiler will give you an error because you are comparing Id<Project> and Id<Person>.
I have A and B classes both implementing interface I.
public interface I
{
int SomeInt { get; }
bool SomeBool { get; }
float SomeFloat { get; }
}
public class A : I
{
public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }
private readonly string _someARelatedStuff;
// Rest of class...
}
public class B : I
{
public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }
private string readonly _someBRelatedStuff;
private double readonly _someOtherBRelatedStuff;
// Rest of class...
}
Sometimes I want to test equality between A and B (usually when comparing lists of A and lists of B) based on the equality of their I properties (SomeInt, SomeBool, SomeFloat), so I implemented IEquatable<I> on both and I compare them based on their shared I properties values.
The problem is that I already have an implementation for GetHashCode() on both A and B that produces different hashes because I'm taking into account additional members.
B does not depend on A so I use interface I to compare them and it has a list of properties with getters.
I read in a StackOverflow answer that:
If you are implementing a class, you should always make sure that two equal objects have the same hashcode.
So does that mean that everytime a class A want to be implement interface I, and I want to be able to compare instances that implement I, I have to make sure the hashcode is calculated in the same way for all instances of I and only use I properties?
I do feel like I'm not intended to implement IEquatable<T> when T is an interface, but my alternatives are:
Using regular inheritance with a base class - I rather avoid inheritance when possible, and this solution won't work if B needs to derive from some framework C class because of single inheritance
Implement equality checks between A and B with a method on either A or B - will create code duplication
Have an equality check method between I instances defined in I - sounds like the best option
Are there any options that I'm missing?
Consider making the a IEqualityComparer<> class to compare the common values.
I have renamed the interface to ICommon for readability
public interface ICommon
{
int SomeInt { get; }
bool SomeBool { get; }
float SomeFloat { get; }
}
public class CommonComparer : IEqualityComparer<ICommon>
{
public bool Equals(ICommon x, ICommon y)
{
return x.SomeInt.Equals(y.SomeInt)
&& x.SomeBool.Equals(y.SomeBool)
&& x.SomeFloat.Equals(y.SomeFloat);
}
public int GetHashCode(ICommon obj)
{
unchecked
{
int hc = -1817952719;
hc = (-1521134295)*hc + obj.SomeInt.GetHashCode();
hc = (-1521134295)*hc + obj.SomeBool.GetHashCode();
hc = (-1521134295)*hc + obj.SomeFloat.GetHashCode();
return hc;
}
}
}
and the program can distinguish between the equal items on two lists.
class Program
{
static void Main(string[] args)
{
var listA = new List<A>
{
new A(1001, true, 1.001f, "A1"),
new A(1002, true, 1.002f, "A2"),
new A(1003, false, 1.003f, "A1"),
new A(1004, false, 1.004f, "A4")
};
var listB = new List<B>
{
new B(1001, true, 1.001f, "B1", 2.5),
new B(1002, false, 1.002f, "B2", 2.8),
new B(1003, true, 1.003f, "B3", 2.9),
new B(1004, false, 1.004f, "B4", 2.9)
};
var common = Enumerable.Intersect(listA, listB, new CommonComparer()).OfType<ICommon>();
Console.WriteLine($"{"SomeInt",-8} {"Bool",-6} {"SomeFloat",-10}");
foreach (var item in common)
{
Console.WriteLine($"{item.SomeInt,-8} {item.SomeBool,-6} {item.SomeFloat,-10}");
}
//SomeInt Bool SomeFloat
//1001 True 1.001
//1004 False 1.004
}
}
and the rest of the code definitions
public class A : ICommon, IEquatable<A>
{
static readonly CommonComparer comparer = new CommonComparer();
public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }
private readonly string _someARelatedStuff;
// Rest of class...
public A(ICommon other, string someARelatedStuff)
: this(other.SomeInt, other.SomeBool, other.SomeFloat, someARelatedStuff)
{ }
public A(int someInt, bool someBool, float someFloat, string someARelatedStuff)
{
this.SomeInt = someInt;
this.SomeBool = someBool;
this.SomeFloat = someFloat;
this._someARelatedStuff = someARelatedStuff;
}
public override string ToString() => _someARelatedStuff;
#region IEquatable Members
public override bool Equals(object obj)
{
if (obj is A other)
{
return Equals(other);
}
return false;
}
public virtual bool Equals(A other)
{
return comparer.Equals(this, other)
&& _someARelatedStuff.Equals(other._someARelatedStuff);
}
public override int GetHashCode()
{
unchecked
{
int hc = comparer.GetHashCode(this);
hc = (-1521134295)*hc + _someARelatedStuff.GetHashCode();
return hc;
}
}
#endregion
}
public class B : ICommon, IEquatable<B>
{
static readonly CommonComparer comparer = new CommonComparer();
public int SomeInt { get; }
public bool SomeBool { get; }
public float SomeFloat { get; }
readonly string _someBRelatedStuff;
readonly double _someOtherBRelatedStuff;
// Rest of class...
public B(ICommon other, string someBRelatedStuff, double someOtherBRelatedStuff)
: this(other.SomeInt, other.SomeBool, other.SomeFloat, someBRelatedStuff, someOtherBRelatedStuff)
{ }
public B(int someInt, bool someBool, float someFloat, string someBRelatedStuff, double someOtherBRelatedStuff)
{
this.SomeInt = someInt;
this.SomeBool = someBool;
this.SomeFloat = someFloat;
this._someBRelatedStuff = someBRelatedStuff;
this._someOtherBRelatedStuff = someOtherBRelatedStuff;
}
public override string ToString() => $"{_someBRelatedStuff}, {_someOtherBRelatedStuff.ToString("g4")}";
#region IEquatable Members
public override bool Equals(object obj)
{
if (obj is B other)
{
return Equals(other);
}
return false;
}
public virtual bool Equals(B other)
{
return comparer.Equals(this, other)
&& _someBRelatedStuff.Equals(other._someBRelatedStuff)
&& _someOtherBRelatedStuff.Equals(other._someOtherBRelatedStuff);
}
public override int GetHashCode()
{
unchecked
{
int hc = comparer.GetHashCode(this);
hc = (-1521134295)*hc + _someBRelatedStuff.GetHashCode();
hc = (-1521134295)*hc + _someOtherBRelatedStuff.GetHashCode();
return hc;
}
}
#endregion
}
So I have two Lists.
List<Farmer> CSVFarmer;
List<Farmer> Farmers;
CSVFarmer List gets its items from a method that will read a csv file.
Farmers List gets its items from a table in a sql database;
Now what I want to do is compare the two lists and return a list of non matching items;
For example if List CSVFarmer has:
FarmerName ContractNumber ContactNumber
John 2468 12345
Mike 13579 15790
And List Farmers has:
FarmerName ContractNumber ContactNumber
Mike 13579 15790
The list being returned should only have one item in it : Farmer John.
Farmer Class:
public class Farmer:INotifyPropertyChanged
{
int _id;
string _firstName;
string _farmerNo;
string _contactNumber;
public Farmer()
{
_firstName = string.Empty;
_farmerNo = string.Empty;
_contactNumber = string.Empty;
}
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string FarmerNo
{
get { return _farmerNo; }
set
{
_farmerNo = value;
OnPropertyChanged("FarmerNo");
}
}
public string ContactNumber
{
get { return _contactNumber; }
set
{
_contactNumber = value;
OnPropertyChanged("ContactNumber");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged (string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
Ive tried this:
public class vmUserManagement
{
public List<Farmer> Farmer { get; set; }
public List<Farmer> CSVFarmers { get; set; }
public List<Farmer> Farmers { get; set; }
public vmUserManagement()
{
CSVFarmers = new List<Farmer>();
Farmers = new List<Farmer>();
Farmer = new List<Farmer>();
}
public List<Farmer> getAllFarmers()
{
Farmers = RepoDapper.getAllFarmers();
return Farmers;
}
public List<Farmer> CSVImportFarmer()
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.DefaultExt = ".csv";
openFile.Filter = "(.csv) | *.csv";
var browseFile = openFile.ShowDialog();
if (browseFile == true)
{
string FilePath = openFile.FileName;
List<Farmer> values = File.ReadAllLines(FilePath).Select(v => FromFarmerCsv(v)).ToList();
CSVFarmers = values;
}
return CSVFarmers;
}
public static Farmer FromFarmerCsv(string csvLine)
{
string[] values = csvLine.Split(',');
Farmer farmer = new Farmer();
farmer.FirstName = values[0];
farmer.FarmerNo = values[1];
farmer.ContactNumber = values[2];
return farmer;
}
public List<Farmer> validateFarmerList()
{
foreach (var a in CSVFarmers)
{
foreach (var b in Farmers)
{
if (a != b)
{
Farmer.Add(a);
}
}
}
return Farmer;
}
}
The problem I'm having is that I will end up with two entries in List Farmer. Both for Farmer John and Farmer Mike. When I should only be getting a List containing Farmer John. Why is that?
I've also tried using Except:
public List<Farmer> validateFarmerList()
{
Farmer = CSVFarmers.Except(Farmers).ToList();
return Farmer;
}
But I still get two items in my Farmer List (Farmer John and Mike) instead of one.
Am I missing something? Any help will be much appreciated.
You have't overridden Equals and GethashCode in your Farmer class. That's why (a != b) doesn't work and also Enumerable.Except fails for the same reason: only references are compared and both are different instances.
How should .NET know that the ContractNumber of the farmer is relevant to identify him? One wayy is to tell it by overriding Equals and GetHashCode:
public class Farmer : IEquatable<Farmer>
{
public string FarmerName { get; set; }
public string ContractNumber { get; set; }
// .... other properties etc
public bool Equals(Farmer other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(ContractNumber, other.ContractNumber);
}
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((Farmer) obj);
}
public override int GetHashCode()
{
return (ContractNumber != null ? ContractNumber.GetHashCode() : 0);
}
}
Now you can use Except:
Farmer = CSVFarmers.Except(Farmers).ToList();
2nd way is to implement a custom IEqualityComparer<Farmer>, f.e if you can't change the Farmer class itself or you don't want to change it's behaviour and just want a custom comparer:
public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}
You can use this comparer in many LINQ methods, for example also in Enumerable.Except:
Farmer = CSVFarmers.Except(Farmers, new FarmerContractComparer()).ToList();
This approach has the advantage that you could provide different comparers for different tasks.
3rd approach: use LINQ and don't create a new class(less reusable and efficient but less work):
Farmer = CSVFarmers.Where(f => !Farmers.Any(f2 => f.ContractNumber == f2.ContractNumber)).ToList();
You will need to either create a class implementing IEqualityComparer<Farmer> and pass an instance of that as a second parameter to .Except() or implement IEquatable<Farmer> on your Farmer class.
The other answer already has a good implementation of the latter. If you always want farmers to be equal when their contract number is equal, use that one.
So if you want your Except work on contract numbers, but in other places you want other criteria, you need the first option:
class FarmerEqualWhenContractEqualComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the farmers' contracts are equal.
return x.ContractNumber == y.ContractNumber;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Farmer farmer)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
return farmer.ContractNumber?.GetHashCode() ?? 0;
}
}
Then you can do:
var changedOrNew = CSVFarmers.Except(Farmers, new FarmerEqualWhenContractEqualComparer()).ToList();
I have a big big class, around 4000 thousands lines, and I think it can be another way to do the following.
It is a class for serialize a message, the nested classes are always with the same structure and overrides the ToString and Equal methods. There are simple classes like the following and complex classes which implements those simples classes in properties.
My idea is something like an extension class who implements the override and extend the simples classes with it
Example class
[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public class ID
{
#region Public Properties
[XmlAttribute("ID")]
public String Value { get; set; }
#endregion
#region Public Methods and Operators
public static bool operator == (ID left, ID right)
{
return Equals(left, right);
}
public static bool operator != (ID left, ID right)
{
return !Equals(left, right);
}
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((ID) obj);
}
public override int GetHashCode ()
{
return (Value != null
? Value.GetHashCode()
: 0);
}
public override String ToString ()
{
return Value;
}
#endregion
#region Methods
protected bool Equals (ID other)
{
return string.Equals(Value, other.Value);
}
#endregion
}
My idea
public class FieldExtension
{
public String Value { get; set; }
#region Public Methods and Operators
public static bool operator == (FieldExtension left, FieldExtension right)
{
return Equals(left, right);
}
public static bool operator != (FieldExtension left, FieldExtension right)
{
return !Equals(left, right);
}
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((ID) obj);
}
public override int GetHashCode ()
{
return (Value != null
? Value.GetHashCode()
: 0);
}
public override String ToString ()
{
return Value;
}
#endregion
#region Methods
protected bool Equals (ID other)
{
return string.Equals(Value, other.Value);
}
#endregion
}
public class ID: FieldExtension
{
#region Public Properties
[XmlAttribute("ID")]
public String Value { get; set; }
#endregion
}
I don't know if this is possible and I just ask if some did that before or something like that or another ideas, if works will save me a lot of work
Thanks in advance
EDIT
Example complex class
[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public class Zählpunkt
{
#region Public Properties
[XmlElement("ID")]
public ID ID { get; set; }
[XmlAttribute("Zählpunkt")]
public String Value { get; set; }
#endregion
..
You would make the base class FieldExtension abstract, and the Value property abstract:
public abstract class FieldExtension
{
public abstract String Value { get; set; }
...
Then in the ID class you override the property:
override public String Value { get; set; }
That makes the code in the base class use the value that you implement in the class that inherits it.
I have a collection of objects where I want to find distinct values based on several properties.
I could do this:
var distinct = myValues.GroupBy(p => new { A = p.P1, B = p.P2 });
But I want to encapsulate the equality sementics. Something like this:
public interface IKey<T>
{
bool KeyEquals(T other);
}
public class MyClass : IKey<MyClass>
{
public string P1 { get; set; }
public string P2 { get; set; }
public bool KeyEquals(MyClass other)
{
if(object.ReferenceEquals(this, other)
return true;
if(other == null)
return false;
return this.P1 == other.P1 && this.P2 == other.P2;
}
}
Is there an O(N) way to get distinct values using my KeyEquals function?
If you can't change MyClass, you can implement an IEqualityComparer:
class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass m1, MyClass m2)
{
return m1.KeyEquals(m2);
}
public int GetHashCode(MyClass m)
{
return (m.P1.GetHashCode() *23 ) + (m.P2.GetHashCode() * 17);
}
}
And pass it to GroupBy
var distinct = myValues.GroupBy(p => p, new MyClassComparer());