Recently I decided to play around with overriding the Equals method. I used mainly the MSDN guideline (the updated one I believe). So my implementation end up like this:
public class EqualityCheck
{
public int Age { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public override bool Equals(object obj)
{
if (null == obj)
{
return false;
}
//If obj is on of the expected type return false
EqualityCheck ec = obj as EqualityCheck;
if (null == ec)
{
return false;
}
//return true if the fields match. This is the place where we can decide what combination should be unique
return (Age == ec.Age) && (Name == ec.Name) && (DateOfBirth == ec.DateOfBirth);
}
public bool Equals(EqualityCheck ec)
{
// If parameter is null return false:
if (ec == null)
{
return false;
}
// Return true if the fields match:
return (Age == ec.Age) && (Name == ec.Name);
}
//How to implement GetHashCode for complex object?
}
And also one child class:
public class EqualityCheckChild : EqualityCheck
{
public int Height { get; set; }
public override bool Equals(System.Object obj)
{
// If parameter cannot be cast to ThreeDPoint return false:
EqualityCheckChild ec1 = obj as EqualityCheckChild;
if (ec1 == null)
{
return false;
}
// Return true if the fields match:
return base.Equals(obj) && Height == ec1.Height;
}
public bool Equals(EqualityCheckChild ec1)
{
// Return true if the fields match:
return base.Equals((EqualityCheck)ec1) && Height == ec1.Height;
}
public static bool operator ==(EqualityCheckChild a, EqualityCheckChild b)
{
if (Equals(a, b))
{
return true;
}
if (a == null || b == null)
{
return false;
}
return a.DateOfBirth == b.DateOfBirth && a.Name == b.Name;
}
public static bool operator !=(EqualityCheckChild a, EqualityCheckChild b)
{
return !(a == b);
}
}
I think this is what MSDN shows as implementation, just with removed casts.
I am using VS2015 and .NET 4.5.2. VS marked the casting as redundant, that's when I look up what's the issue with this. I've read about the possible infinity loop and just out of curiosity I decided to recreate it with the code above. However I can't reproduce it.
Since this is not copy-paste code. At least not literally. I wrote it by hand trying to understand what I am doing so I guess it's possible to have some mismatch with the original code which leads to this. But still my question remains - how to reproduce the problem which is also mentioned in the MSDN article?
Attempt to compare an instance of EqualityCheckChild to null.
EqualityCheckChild foo = new EqualityCheckChild();
Console.WriteLine(foo == null);
This code snippet will cause a StackOverflowException, because in the operator== method, if (a == null || b == null) calls itself.
Related
So I created a class like this:
public class ClassName
{
public int ID;
public String n_1 {get; set; }
public String n_2 {get; set; }
// ....
public String n_x {get; set; }
}
Later in my code I compare 2 ClassName objects:
ClassName Item_1 /*...*/ ;
ClassName Item_2 /*...*/ ;
Like this:
if (Item_1 != Item_2 && Item_1.n_a == Item_2.n_a)
{
//do something
}
Now my Problem is that Item_1.ID and Item_2.ID should be ignored. Is there a easy way to do this? The only solution I came up with is a no brainer of like
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
Override the object.Equals method in your class and specify how you want it to be compared.
Then use !Item1.Equals(Item2) instead of !=
https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.7.2
The easy way is what you are actually doing , just create a method like this
public bool Method(ClassName Item_1,ClassName Item2)
{
bool check=false;
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
check=true
return check
}
To perform something like Item_1 != Item_2 or Item_1 == Item_2 and get all properties (or the ones you want) to be compared, you need to implement your own Equals method and ==, != operators overloads.
== operator, from MSDN:
For reference types other than string, == returns true if its two operands refer to the same object.
For that you can start with some straightforward solution, to implement IEquatable<T> interface, fill your comparing logic inside Equals method and overload == and != operators internally calling your type-safe Equals method:
public class ClassName : IEquatable<ClassName>
{
public int ID;
public String n_1 { get; set; }
public String n_2 { get; set; }
// ....
public String n_x { get; set; }
public static bool operator ==(ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return Equals(obj1, obj2);
return obj1.Equals(obj2);
}
public static bool operator != (ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return !Equals(obj1, obj2);
return !obj1.Equals(obj2);
}
public bool Equals(ClassName obj)
{
if (obj == null) return false;
return (n_1 == obj.n_1) && (n_2 == obj.n_2) && (n_x == obj.n_x); //you can ignore ID here
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
ClassName classNameObj = obj as ClassName;
if (classNameObj == null)
return false;
else
return Equals(classNameObj);
}
public override int GetHashCode()
{
//This code was generated by VS ide, you can write your own hashing logic
var hashCode = 1032198799;
hashCode = hashCode * -1521134295 + ID.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_1);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_2);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_x);
return hashCode;
}
}
Then you can check in your if:
if (Item_1 != Item_2)
{
//Do Something
}
For Equals method you should stick to Guidelines for Overriding Equals() and Operator == .
References: == Operator, != Operator, IEquatable Interface, IEquatable.Equals(T) Method, Guidelines for Overriding Equals() and Operator ==
I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}
I have the following code:
public enum ClassType
{
I,
II,
}
public enum LocationType
{
A,
B
}
public class Person
{
public LocationType LocType
{ get; set; }
public ClassType ClaType
{ get; set; }
public override bool Equals(object obj)
{
Person obPer = obj as Person;
if (obPer == null)
return false;
if (LocType != obPer.LocType)
return false;
if (ClaType != obPer.ClaType)
return false;
return true;
}
public override int GetHashCode()
{
return LocType.GetHashCode()^ClaType.GetHashCode();
}
}
static void Main(string[] args)
{
var p1 = new Person()
{
ClaType = ClassType.I,
LocType = LocationType.A
};
var p2 = new Person()
{
ClaType = ClassType.I,
LocType = LocationType.A
};
bool isEqual1 = p1.Equals(p2); //true
bool getHashCodeNum = p1.GetHashCode() == p2.GetHashCode(); //true
bool isEqual2 = p1 == p2; //false
}
I find that isEqual1=true, getHashCodeNum=true, but isEqual2=false.
I would expect that since I already override Equals and GetHashCode, then the operator == should automatically follow the behavior of Equals, but this is not so. Any reason?
An == is an operator. You can overload the == operator over two Persons as follows:
public class Person {
//..
public static bool operator == (Person a, Person b)
{
if (Object.ReferenceEquals(a,null) && Object.ReferenceEquals(b,null))
return true;
if (Object.ReferenceEquals(a,null) || Object.ReferenceEquals(a,null))
return false;
return a.LocType == b.LocType && a.ClaType != b.ClaType;
}
public static bool operator != (Person a, Person b)
{
return ! (a == b);
}
}
== and != are pairs: you need to implement != if you implement == and vice versa, otherwise you get the error:
error CS0216: The operator Person.operator ==(Person, Person) requires a matching operator != to also be defined
Now when you compare two Persons it should work. Mind however that you do not override equality operators, you overload them. So the compiler picks the == implementation (this is not done at runtime through a dynamic binding). As a result:
bool isEqual2 = p1 == p2; //true
bool isEqual3 = (object) p1 == p2; //false
bool isEqual4 = p1 == (object) p2; //false
bool isEqual5 = (object) p1 == (object) p2; //false
By default the == over two objects is reference equality so only if the two arguments are Persons here, we check whether the two persons are equivalent.
It is therefore probably better to use Equals(..) if you want to check equality with a dynamic binding.
I have a SortedDictionary<Package, List<string>>. Following is the Package class:
using System;
namespace GetPackageInfo
{
public class Package : IComparable, IEquatable<Package>
{
public string PackageName;
public string Version;
public Package()
{
}
public Package(string packageName)
{
this.PackageName = packageName;
}
public override int GetHashCode()
{
unchecked
{
int result = 17;
result = result * 23 + ((PackageName != null) ? this.PackageName.GetHashCode() : 0);
result = result * 23 + ((Version != null) ? this.Version.GetHashCode() : 0);
return result;
}
}
public bool Equals(Package other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(this.PackageName, other.PackageName) &&
Equals(this.Version, other.Version);
}
public override bool Equals(object obj)
{
Package temp = obj as Package;
if (temp == null)
return false;
return this.Equals(temp);
}
public override string ToString()
{
return string.Format("PackageName: {0}, Version: {1}", PackageName, Version);
}
public int CompareTo(object obj)
{
if (obj == null)
return 1;
if (obj != null)
return (Equals(obj) ? -1 : 1);
else
throw new ArgumentException("Object is not a Temperature");
}
Whenever I do a Contains or ContainsKey on the SortedDictionary, it does not work even when the name and version is the same.
if (nugetPackagesInfo.Keys.Any(p => p.Equals(package)))
{
//List<string> currPackage;
//nugetPackagesInfo.TryGetValue(package, out currPackage);
if (!nugetPackagesInfo[package].Contains(packageFile))
{
nugetPackagesInfo[package].Add(packageFile);
}
nuGetPackagesInfo is my dictionary object. The Any returns true though. But once it is passed and gets to nugetPackagesInfo[package], then it throws the KeyNotFoundException. Can you please help me figure it out? Is my CompareTo not correct?
Your implementation of CompareTo doesn't seem to be correct. In fact, you don't implement any ordering of packages. You should, most likely, order packages by name, and if equal by version.
The core of Package.CompareTo should look like this (simplified; not taking care of other == null):
// try name ordering
int nameOrdering = this.Name.CompareTo(other.Name);
// names not equal ⇒ ordering is clear and no need to inspect further
if (nameOrdering != 0)
{
return nameOrdering;
}
// names are equal ⇒ resort to version ordering
else
{
return this.Version.CompareTo(other.Version);
}
You should also read the documentation for String.CompareTo() because of its culture-specific semantics.
Change CompareTo and GetHashCode to these implementations.
public int CompareTo(object obj)
{
if (obj == null)
return 1;
return this.ToString().CompareTo(obj.ToString());
}
public override int GetHashCode()
{
unchecked
{
return ((PackageName != null ? PackageName.GetHashCode() : 0)*397) ^ (Version != null ? Version.GetHashCode() : 0);
}
}
public override string ToString()
{
return string.Format("PackageName: {0}, Version: {1}", PackageName??"", Version ?? "");
}
CompareTo - see the documentation. By using ToString() you get a comparison on the package name and then the version without having to do your own checks. Just make sure that ToString is correct (I added null check so it does not throw an Exception).
GetHashCode - not sure where you got your implementation from. You need to make sure that the hashcode is always unique for that item unless they truely are equal. This is the implementation I found on this previous answer on SO, see the last edit in the answer..
Your CompareTo method should work as this:
return -1 if this is smaller than obj
return 1 if this is bigger than obj and
most important: return 0 if this equals obj
How does the == operator really function in C#? If it used to compare objects of class A, will it try to match all of A's properties, or will it look for pointers to the same memory location (or maybe something else)?
Let's create a hypothetical example. I'm writing an application that utilizes the Twitter API, and it has a Tweet class, which has all the properties of a single tweet: text, sender, date&time, source, etc. If I want to compare objects of class Tweet for equivalence, can I just use:
Tweet a, b;
if (a == b)
{
//do something...
}
Will that check for equivalence of all the properties of the Tweet class between a and b?
If not, would the correct approach be to overload the == operator to explicitly check for equivalence of all the fields?
UPDATE: From the first two answers, am I right in assuming:
If the == operator or Equals method is not overloaded for a class, the == operator for the object class is used.
The == operator for the object class checks for equality in memory location.
I have to overload the == operator or the Equals method to accomplish this task.
In the overload, I have to check for equivalence in properties manually, so there is no way to do it semi-automatically, say, in a loop, right?
UPDATE #2: Yuriy made a comment that it is possible to do check for equivalence in properties in the == operator with reflection. How can this be done? Could you give me some sample code? Thanks!
For reference types, the default implementations of both the == operator and the Equals() method will simply check that both objects have the same reference, and are therefore the same instance.
If you want to check the contents of two different objects are equal then you must write the code to do it yourself, one way or another. It would be possible to do with reflection (the MbUnit test framework does something along these lines) but with a heavy performance penalty and a good chance that it wouldn't do quite what you expected anyway, and you should implement == or Equals and GetHashCode by hand.
MSDN has a good example of how to do it:
public override bool Equals(object o)
{
try
{
return (bool) (this == (DBBool) o);
}
catch
{
return false;
}
}
Then you overload the == and !=:
// Equality operator. Returns dbNull if either operand is dbNull,
// otherwise returns dbTrue or dbFalse:
public static DBBool operator ==(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value == y.value? dbTrue: dbFalse;
}
// Inequality operator. Returns dbNull if either operand is
// dbNull, otherwise returns dbTrue or dbFalse:
public static DBBool operator !=(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value != y.value? dbTrue: dbFalse;
}
And don't forget to overload the GetHash method.
Edit:
I wrote the following quick sample for using reflection in a compare. This would have to be much more comprehensive, I might try doing a blog on it if people want me to:
public class TestEquals
{
private int _x;
public TestEquals(int x)
{
this._x = x;
}
public override bool Equals(object obj)
{
TestEquals te = (TestEquals)obj;
if (te == null) return false;
foreach (var field in typeof(TestEquals)
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
if (!field.GetValue(this).Equals(field.GetValue(te)))
return false;
}
return true;
}
}
The proper approach is the overload the equals method of the Tweet class in addition to the == operator, as described here.
Will that check for equivalence of all the properties of the Tweet class between a and b?
No
If not, would the correct approach be to overload the == operator to explicitly check for equivalence of all the fields?
You can either overload the == operator, or overload the Equals function.
Edit
#Yuriy gave a good example for compating all the non public variables. Since i also wrote an example, here it is (mine compares properties)
class TwitterItem
{
private string myValue = "default value";
public string Value1
{
get { return myValue; }
set { myValue = value; }
}
public string Value2
{
get { return myValue; }
set { myValue = value; }
}
public string Value3
{
get { return myValue; }
set { myValue = value; }
}
public override bool Equals(object obj)
{
if (base.Equals(obj)) return true;
Type type = typeof(TwitterItem);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (false == property.GetValue(this, null).Equals(property.GetValue(obj, null)))
return false;
}
return true;
}
}
You can compare the properties using reflection:
var a = new Entity() { Name = "test", ID = "1" };
var b = new Entity() { Name = "test", ID = "1" };
var c = new Entity() { Name = "test", ID = "2" };
System.Diagnostics.Debug.WriteLine(a.Equals(b));//Returns true
System.Diagnostics.Debug.WriteLine(a.Equals(c));//Returns false
public class Entity
{
public string Name { get; set; }
public string ID { get; set; }
public override bool Equals(object obj)
{
var t = obj.GetType();
foreach (var p in t.GetProperties())
{
if (t.GetProperty(p.Name).GetValue(obj, null) != t.GetProperty(p.Name).GetValue(this, null))
return false;
}
return true;
}
}