This question already has answers here:
C# equality checking
(4 answers)
Closed 7 years ago.
I have an Object called User. Here's my class:
public class User
{
public string Name { get; set; }
public string IdNumber { get; set; }
public string OrgName { get; set; }
public string AcctCode { get; set; }
}
I create TWO objects that are exactly the same.
I want to see if their Equal, however, I get False back.
User user1 = new User()
{
Name = "Test User",
IdNumber = "1000354",
OrgName = "North",
AcctCode = "FTW"
};
User user2 = new User()
{
Name = "Test User",
IdNumber = "1000354",
OrgName = "North",
AcctCode = "FTW"
};
var doesEqual = user1.Equals(user2);
Console.WriteLine(doesEqual); // Returns FALSE
I also have TWO lists that have same objects in them, except for one.
When I do the Except, it doesn't want to work right and returns 131.
List<User> ListOne; // Contains 131 User objects
List<User> ListTwo; // Contains 130 User objects
var difference = ListOne.Except(ListTwo);
Console.WriteLine(difference); // Returns 131
What am I doing wrong??
By default, reference types will compare references. Meaning that your 2 separate instances, even though their fields have the same value, are different object and will compare as inequal. You can change this behavior by having your class implement IEquatable<T> to "teach" it how to compare any way you want.
A great example, that almost matches your usage, can be found on MSDN.
Class default equality criteria is by reference. It should return false.
You can override Equals method (and then you will have to implement GetHashCode) to whatever logic you want.
public override bool Equals(object obj)
{
if(!(obj is User))
{
return false;
}
User user= obj as User;
return user.Name == Name && user.IdNumber == IdNumber && user.OrgName == OrgName && user.AcctCode == AcctCode;
}
public override int GetHashCode()
{
return IdNumber.GetHashCode();
}
Despite the fact that all the properties of your objects have the same value, they are two completely different object. This user1 holds a reference to the heap for an object with some values. The same holds for user2. That being said this are two different references to the heap.
If you don't override the Equals method, then the equality is based on the references. So the objects are different, even when they have exactly the same values for all of their properties.
They are not equal because User is a class (i.e. a "reference type"). For reference types, .Equals compares reference equality, and the two instances user1 and user2 are two separate references.
The problem is this line:
user1.Equals(user2)
This line is asking if the user1 object the same as the user2 object. They have the same values but they are not the same object in memory. If you want to compare the values of these two objects you can override the Equals method like so:
public static override Equals(User user2)
{
return (this.Name == user2.Name &&
this.IdNumber == user2.IdNumber &&
this.AcctCode == user2.AcctCode &&
this.OrgName == user2.OrgName)
}
Your class User does not implement anything to tell the runtime when two objects are equal. The simpliest way to acchieve this is by overwriting the two Methods Equals and GetHashCode like this:
public override bool Equals(object obj)
{
User other = obj as User;
if( other == null ) return false;
return other.Name == Name && other.IdNumber == IdNumber && other.OrgName == OrgName && other.AcctCode == AcctCode;
}
public override int GetHashCode()
{
return IdNumber.GetHashCode();
}
Related
this is my Clients class:
public class Clients
{
public string Email { get; set; }
public string Name { get; set; }
public Clients(string e, string n)
{
Email = e;
Name = n;
}
I want to make a new list which contains the same clients from List A and List B .
For example:
List A - John, Jonathan, James ....
List B - Martha, Jane, Jonathan ....
Unsubscribers - Jonathan
public static List<Clients> SameClients(List<Clients> A, List<Clients> B)
{
List<Clients> Unsubscribers = new List<Clients>();
Unsubscribers = A.Intersect(B).ToList();
return Unsubscribers;
}
However for some reasons I get empty list and I have no idea what's wrong.
The problem is that when you are comparing objects Equals and Gethashcode are used to compare them. You can override these two methods and provide your own implementation based on your needs...there is already an answer below covering how to override these two methods
However, normally I prefer to keep my entities/models (or whatever you want to call them) very simple and keep comparison implementation details away from my models. In that case, you can implement an IEqualityComparer<TSource> and use an overload of Intersects that takes in an IEqualityComparer
Here's an example implementation of IEqualityComprarer based on only the Name property...
public class ClientNameEqualityComparer : IEqualityComparer<Clients>
{
public bool Equals(Clients c1, Clients c2)
{
if (c2 == null && c1 == null)
return true;
else if (c1 == null | c2 == null)
return false;
else if(c1.Name == c2.Name)
return true;
else
return false;
}
public int GetHashCode(Client c)
{
return c.Name.GetHashCode();
}
}
Basically, the implementation above only cares about the Name property, if two instances of Clients have the same value for the Name property, then they are considered equal.
Now you can do the followig...
A.Intersect(B, new ClientNameEqualityComparer()).ToList();
And that will produce the results you are expecting...
Intersect uses GetHashCode and Equals by default, but you haven't overriden it, so Object.Equals is used which just compares references. Since all your client-instances are initialized with new they are separate instances even if they have equal values. That's why Intersect "thinks" that there are no common clients.
So you have several options.
implement a custom IEqualityComparer<Clients> and pass that to Intersect(or many other LINQ methods). This has the advantage that you could implement different comparer for different requirements and you don't need to modify the original class
let Clients override Equals and GetHashCode and /or
let Clients implement IEquatable<Clients>
For example(showing the last two because other answer showed already IEqualityComparer<T>):
public class Clients : IEquatable<Clients>
{
public string Email { get; set; }
public string Name { get; set; }
public Clients(string e, string n)
{
Email = e;
Name = n;
}
public override bool Equals(object obj)
{
return obj is Clients && this.Equals((Clients)obj);
}
public bool Equals(Clients other)
{
return Email == other?.Email == true
&& Name == other?.Name == true;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 23 + (Email?.GetHashCode() ?? 0);
hash = hash * 23 + (Name?.GetHashCode() ?? 0);
return hash;
}
}
}
Worth reading:
Differences between IEquatable<T>, IEqualityComparer<T>, and overriding .Equals() when using LINQ on a custom object collection?
I have some classes that contain several fields. I need to compare them by value, i.e. two instances of a class are equal if their fields contain the same data. I have overridden the GetHashCode and Equals methods for that.
It can happen that these classes contain circular references.
Example: We want to model institutions (like government, sports clubs, whatever). An institution has a name. A Club is an institution that has a name and a list of members. Each member is a Person that has a name and a favourite institution. If a member of a certain club has this club as his favourite institution, we have a circular reference.
But circular references, in conjunction with value equality, lead to infinite recursion. Here is a code example:
interface IInstitution { string Name { get; } }
class Club : IInstitution
{
public string Name { get; set; }
public HashSet<Person> Members { get; set; }
public override int GetHashCode() { return Name.GetHashCode() + Members.Count; }
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
return Name.Equals(other.Name) && Members.SetEquals(other.Members);
}
}
class Person
{
public string Name { get; set; }
public IInstitution FavouriteInstitution { get; set; }
public override int GetHashCode() { return Name.GetHashCode(); }
public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null)
return false;
return Name.Equals(other.Name)
&& FavouriteInstitution.Equals(other.FavouriteInstitution);
}
}
class Program
{
public static void Main()
{
Club c1 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p1 = new Person { Name = "Johnny", FavouriteInstitution = c1 }
c1.Members.Add(p1);
Club c2 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p2 = new Person { Name = "Johnny", FavouriteInstitution = c2 }
c2.Members.Add(p2);
bool c1_and_c2_equal = c1.Equals(c2); // StackOverflowException!
// c1.Equals(c2) calls Members.SetEquals(other.Members)
// Members.SetEquals(other.Members) calls p1.Equals(p2)
// p1.Equals(p2) calls c1.Equals(c2)
}
}
c1_and_c2_equal should return true, and in fact we (humans) can see that they are value-equal with a little bit of thinking, without running into infinite recursion. However, I can't really say how we figure that out. But since it is possible, I hope that there is a way to resolve this problem in code as well!
So the question is: How can I check for value equality without running into infinite recursions?
Note that I need to resolve circular references in general, not only the case from above. I'll call it a 2-circle since c1 references p1, and p1 references c1. There can be other n-circles, e.g. if a club A has a member M whose favourite is club B which has member N whose favourite club is A. That would be a 4-circle. Other object models might also allow n-circles with odd numbers n. I am looking for a way to resolve all these problems at once, since I won't know in advance which value n can have.
An easy workaround (used in RDBMS) is to use a unique Id to identify a Person(any type). Then you don't need to compare every other property and you never run into such cuircular references.
Another way is to compare differently in Equals, so provide the deep check only for the type of the Equals and not for the referenced types. You could use a custom comparer:
public class PersonNameComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
if(object.ReferenceEquals(x, y)) return true;
return x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj?.Name?.GetHashCode() ?? int.MinValue;
}
}
Now you can change the Equals implementation of Club to avoid that the Members(Persons) will use their deep check which includes the institution but only their Name:
public override bool Equals(object obj)
{
if (Object.ReferenceEquals(this, obj))
return true;
Club other = obj as Club;
if (other == null)
return false;
var personNameComparer = new PersonNameComparer();
return Name.Equals(other.Name)
&& Members.Count == other.Members.Count
&& !Members.Except(other.Members, personNameComparer).Any();
}
You notice that i can't use SetEquals because there is no overload for my custom comparer.
Following the suggestion of Dryadwoods, I changed the Equals methods so that I can keep track of the items that were already compared.
First we need an equality comparer that checks reference equality for corresponding elements of pairs:
public class ValuePairRefEqualityComparer<T> : IEqualityComparer<(T,T)> where T : class
{
public static ValuePairRefEqualityComparer<T> Instance
= new ValuePairRefEqualityComparer<T>();
private ValuePairRefEqualityComparer() { }
public bool Equals((T,T) x, (T,T) y)
{
return ReferenceEquals(x.Item1, y.Item1)
&& ReferenceEquals(x.Item2, y.Item2);
}
public int GetHashCode((T,T) obj)
{
return RuntimeHelpers.GetHashCode(obj.Item1)
+ 2 * RuntimeHelpers.GetHashCode(obj.Item2);
}
}
And here is the modified Equals method of Club:
static HashSet<(Club,Club)> checkedPairs
= new HashSet<(Club,Club)>(ValuePairRefEqualityComparer<Club>.Instance);
public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;
if (!Name.Equals(other.Name))
return;
if (checkedPairs.Contains((this,other)) || checkedPairs.Contains((other,this)))
return true;
checkedPairs.Add((this,other));
bool membersEqual = Members.SetEquals(other.Members);
checkedPairs.Clear();
return membersEqual;
}
The version for Person is analogous. Note that I add (this,other) to checkedPairs and check if either (this,other) or (other,this) is contained because it might happen that after the first call of c1.Equals(c2), we end up with a call of c2.Equals(c1) instead of c1.Equals(c2). I am not sure if this actually happens, but since I can't see the implementation of SetEquals, I believe it is a possibility.
Since I am not happy with using a static field for the already checked pairs (it will not work if the program is concurrent!), I asked another question: make a variable last for a call stack.
For the general case that I am interested in
-- where we have classes C1, ..., Cn where each of these classes can have any number of VALUES (like int, string, ...) as well as any number of REFERENCES to any other classes of C1, ..., Cn (e.g. by having for each type Ci a field ICollection<Ci>) --
the question "Are two objects A and B equal?", in the sense of equality that I described here,
seems to be EQUIVALENT to
the question "For two finite, directed, connected, colored graphs G and H, does there exist an isomorphism from G to H?".
Here is the equivalence:
graph vertices correspond to objects (class instances)
graph edges correspond to references to objects
color corresponds to the conglomerate of values and the type itself (i.e. colors of two vertices are the same if their corresponding objects have the same type and the same values)
That's an NP-hard question, so I think I'm going to discard my plan to implement this and go with a circular-reference-free approach instead.
I am trying to use FluentAssertions to combine collection and object graph comparison assertions.
I have the following class.
public class Contract
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Which are returned in a collection, like so.
ICollection<Contract> contracts = factory.BuildContracts();
I then want to make sure that collection contains only specific Contract objects.
contracts.Should().Contain(new Contract() { Id = id1, Name = "A" });
This doesn't work, I'm believe because Contain is using object.Equals rather than object graph comparison, (as provided by ShouldBeEquivalentTo).
I also need to assert that the collection doesn't contain a specific object, i.e.
contracts.Should().NotContain(new Contract() { Id = id2, Name = "B" });
Effectively given a collection containing an unknown number of items, I want to ensure that; it contains a number of specific items, and that it doesn't contain a number of specific items.
Can this be achieved using the functions provided by FluentAssertions?
As a side note, I don't want to override object.Equals for the reasons discussed here. Should I be using IEquatable to ease testing of factories?
It does use the object.Equals as far as I can tell from documentation and my experience using the framework.
In scenarios like this I tend to use an expression predicate as referenced in the collections documentation for v3.0 and higher.
The following example shows how to make sure that collection contains only specific Contract objects and also assert that the collection doesn't contain a specific object.
[TestMethod]
public void FluentAssertions_Should_Validate_Collections() {
//Arrange
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var list = new List<Contract>{
new Contract() { Id = id1, Name = "A" },
new Contract() { Id = Guid.NewGuid(), Name = "B"}
};
var factoryMock = new Mock<IContractFactory>();
factoryMock.Setup(m => m.BuildContracts()).Returns(list);
var factory = factoryMock.Object;
//Act
var contracts = factory.BuildContracts();
//Assert
contracts.Should()
.HaveCount(list.Count)
.And.Contain(c => c.Id == id1 && c.Name == "A")
.And.NotContain(c => c.Id == id2 && c.Name == "B");
}
You can override your contract Equals which will then be used and your Unit test should pass happily.
If you're using random Guids it might be an issue, but it seems like you're using a predefined one.
Give the following a try:
protected bool Equals(Contract other)
{
return Id.Equals(other.Id) && string.Equals(Name, other.Name);
}
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((Contract) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Id.GetHashCode()*397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
and as you can see, it passes the test:
Alternatively to Nkosi's answer, you could still use ShouldBeEquivalentTo by building the expecation.
I have two collections and I want to loop through each element and compare the corresponding elements in each collection for equality, thus determining if the collections are identical.
Is this possible with a foreach loop or must I use a counter and access the elements by index?
Generally speaking is there a preferred method for comparing collections for equality, like overloading an operator?
TIA.
You can use .SequenceEqual method which is used for this purpose. Read More.
Examples below if link is down or removed for some reason.
Determines whether two sequences are equal by comparing the elements
by using the default equality comparer for their type.
The SequenceEqual(IEnumerable, IEnumerable)
method enumerates the two source sequences in parallel and compares
corresponding elements by using the default equality comparer for
TSource, Default. The default equality comparer, Default, is used to
compare values of the types that implement the IEqualityComparer
generic interface. To compare a custom data type, you need to
implement this interface and provide your own GetHashCode and Equals
methods for the type.
class Pet
{
public string Name { get; set; }
public int Age { get; set; }
}
public static void SequenceEqualEx1()
{
Pet pet1 = new Pet { Name = "Turbo", Age = 2 };
Pet pet2 = new Pet { Name = "Peanut", Age = 8 };
// Create two lists of pets.
List<Pet> pets1 = new List<Pet> { pet1, pet2 };
List<Pet> pets2 = new List<Pet> { pet1, pet2 };
bool equal = pets1.SequenceEqual(pets2);
Console.WriteLine(
"The lists {0} equal.",
equal ? "are" : "are not");
}
/*
This code produces the following output:
The lists are equal.
*/
If you want to compare the actual data of the objects in the sequences
instead of just comparing their references, you have to implement the
IEqualityComparer generic interface in your class. The following
code example shows how to implement this interface in a custom data
type and provide GetHashCode and Equals methods.
public class Product : IEquatable<Product>
{
public string Name { get; set; }
public int Code { get; set; }
public bool Equals(Product other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return Code.Equals(other.Code) && Name.Equals(other.Name);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the Name field if it is not null.
int hashProductName = Name == null ? 0 : Name.GetHashCode();
//Get hash code for the Code field.
int hashProductCode = Code.GetHashCode();
//Calculate the hash code for the product.
return hashProductName ^ hashProductCode;
}
}
Usage:
Product[] storeA = { new Product { Name = "apple", Code = 9 },
new Product { Name = "orange", Code = 4 } };
Product[] storeB = { new Product { Name = "apple", Code = 9 },
new Product { Name = "orange", Code = 4 } };
bool equalAB = storeA.SequenceEqual(storeB);
Console.WriteLine("Equal? " + equalAB);
/*
This code produces the following output:
Equal? True
*/
I've solved a problem I was having but although I've found out how something works (or doesn't) I'm not clear on why.
As I'm the type of person who likes to know the "why" I'm hoping someone can explain:
I have list of items and associated comments, and I wanted to differentiate between admin comments and user comments, so I tried the following code:
User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole)
{
//do stuff
}
else
{
// do other stuff
}
Stepping through the code showed that although it had the correct Role object, it didn't recognise the role in the commentUser.Roles
The code that eventually worked is:
if(commentUser.Roles.Any(x=>x.Name == "admin"))
{
//do stuff
}
I'm happy with this because it's less code and in my opinion cleaner, but I don't understand how contains didn't work.
Hoping someone can clear that up for me.
This is probably because you didn't override the equality comparisons (Equals, GetHashCode, operator==) on your Role class. Therefore, it was doing reference comparison, which really isn't the best idea, as if they're not the same object, it makes it think it's a different. You need to override those equality operators to provide value equality.
You have to override Equals (and always also GetHashCode then) if you want to use Contains. Otherwise Equals will just compare references.
So for example:
public class Role
{
public string RoleName{ get; set; }
public int RoleID{ get; set; }
// ...
public override bool Equals(object obj)
{
Role r2 = obj as Role;
if (r2 == null) return false;
return RoleID == r2.RoleID;
}
public override int GetHashCode()
{
return RoleID;
}
public override string ToString()
{
return RoleName;
}
}
Another option is to implement a custom IEqualityComparer<Role> for the overload of Enumerable.Contains:
public class RoleComparer : IEqualityComparer<Role>
{
public bool Equals(Role x, Role y)
{
return x.RoleID.Equals(y.RoleID);
}
public int GetHashCode(Role obj)
{
return obj.RoleID;
}
}
Use it in this way:
var comparer = new RoleComparer();
User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole, comparer))
{
// ...
}
When using the Contains-method, you check if the the array Roles of the user-object contains the object you have retrieved from the database beforehand. Though the array contains an object for the role "admin" it does not contain the exact object you fetched before.
When using the Any-method you check if there is any role having the name "admin" - and that delivers the expected result.
To get the same result with the Contains-method implement the IEquatable<Role>-interface on the role-class and compare the name to check whether two instances have actually the same value.
It will be your equality comparison for a Role.
The object in commentUserRole is not the same object as the one you are looking for commentUser.Roles.
Your context object will create a new object when you select from it and populate your Roles property with a collection of new Roles. If your context is not tracking the objects in order to return the same object when a second copy is requested then it will be a different object even though all the properties may be the same. Hence the failure of Contains
Your Any clause is explicitly checking the Name property which is why it works
Try making Role implement IEquatable<Role>
public class Role : IEquatable<Role> {
public bool Equals(Role compare) {
return compare != null && this.Name == compare.Name;
}
}
Whilst MSDN shows you only need this for a List<T> you may actually need to override Equals and GetHashCode to make this work
in which case:
public class Role : IEquatable<Role> {
public bool Equals(Role compare) {
return compare != null && this.Name == compare.Name;
}
public override bool Equals(object compare) {
return this.Equals(compare as Role); // this will call the above equals method
}
public override int GetHashCode() {
return this.Name == null ? 0 : this.Name.GetHashCode();
}
}