Click here to Skip to main content
16,016,168 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have some questions please for the following pieces of source code. It's something new for me and I don't understand a lot of things.
Let's get started!

C#
public class Person : IEquatable<Person>
{
    public string Name { get; set; }
    public int Age { get; set; }

    // override Equals method
    public bool Equals(Person other)
    {
        if (other == null) return false;
        return (this.Name == other.Name 
        			&& this.Age == other.Age);
    }
}

public class PersonComparer : IEqualityComparer<Person>
{
	public bool Equals(Person x, Person y)
	{
		if (x == null || y == null) return false;
		return (x.Name == y.Name && x.Age == y.Age);
	}

   //A hash function is used to quickly generate a number (hash code) that 
   //corresponds to the value of an object. 
	public int GetHashCode(Person obj)
	{
		if (obj == null) return 0;
		return obj.Name.GetHashCode()
					^ obj.Age.GetHashCode();
	}
}


In the second class PersonComparer I don't understand the GetHashCode function, what it does and especially this line of code
return obj.Name.GetHashCode()
					^ obj.Age.GetHashCode();


C#
<pre>
void Main()
{
	var list3 = new List<Person>
	{
		new Person { Name = "Alice", Age = 25 },
		new Person { Name = "Bob", Age = 30 },
		new Person { Name = "Charlie", Age = 35 },
		new Person { Name = "Eve", Age = 50 }
	};

	var list4 = new List<Person>
	{
		new Person { Name = "Bob", Age = 30 },
		new Person { Name = "David", Age = 35 },
		new Person { Name = "Eve", Age = 40 }
	};

	//With Lambda I am getting what I want
	var Lambda = list3.Select(x=> list4.Where(y=> x.Equals(y))).Where(w=> w.Any()).Single();
		
    //But with list3list4CommonEquatable I am not getting any intersection
	Var list3list4CommonEquatable = list3.Intersect(list4);
	
	var comparer = new PersonComparer();

	var commonPeople = list3.Intersect(list4, comparer);	
}


Second question :
if you try this, you will see that with following statement, I am able, to get the intersection results between list3 and list4.
var Lambda = list3.Select(x=> list4.Where(y=> x.Equals(y))).Where(w=> w.Any()).Single();


But this statement
C#
Var list3list4CommonEquatable = list3.Intersect(list4);
doesn't work at all to give me the common elements between list3 and list 4
Why does this happen and could we correct it? It is supposed to be working, but it doesn't.

And Last question about this:
C#
var comparer = new PersonComparer();
var commonPeople = list3.Intersect(list4, comparer);

How does a comparer work? What exactly it does? And why Equals function takes place when we call Intersect? If call an exception for example would Equals function take place in step over running?

I found these piece of code in the site Linq Intersect() - get common elements from 2 lists[^] and I feel confused to understand in order to use it. So any explanation will be much appreciated.
Thank you so much in advanced.

What I have tried:

C#
<pre>public class Person : IEquatable<Person>
{
	public string Name { get; set; }
	public int Age { get; set; }

	// override Equals method
	public bool Equals(Person other)
	{
		if (other == null) return false;
		return (this.Name == other.Name
					&& this.Age == other.Age);
	}
}
public class PersonComparer : IEqualityComparer<Person>
{
	public bool Equals(Person x, Person y)
	{
		if (x == null || y == null) return false;
		return (x.Name == y.Name && x.Age == y.Age);
	}

//A hash function is used to quickly generate a number (hash code) that corresponds to the value of an object. 
	public int GetHashCode(Person obj)
	{
		if (obj == null) return 0;
		return obj.Name.GetHashCode()
					^ obj.Age.GetHashCode();
	}
}

void Main()
{
	var list3 = new List<Person>
	{
		new Person { Name = "Alice", Age = 25 },
		new Person { Name = "Bob", Age = 30 },
		new Person { Name = "Charlie", Age = 35 },
		new Person { Name = "Eve", Age = 50 }
	};

	var list4 = new List<Person>
	{
		new Person { Name = "Bob", Age = 30 },
		new Person { Name = "David", Age = 35 },
		new Person { Name = "Eve", Age = 40 }
	};


	list3.Dump();
	list4.Dump();

	var Lambda = list3.Select(x=> list4.Where(y=> x.Equals(y))).Where(w=> w.Any()).Single();
	//var comparerP = new Person();
	
	list3.GetHashCode().Dump("HashCode");
	list3.ToHashSet().Dump("ToHashSet");
	list3.FirstOrDefault().GetHashCode().Dump("frstHashCode");
	list3.FirstOrDefault().Name.GetHashCode().Dump("frstNameHashCode");
	list3.FirstOrDefault().Age.GetHashCode().Dump("frstAgeHashCode");
	var test =list3.FirstOrDefault().Name.GetHashCode()
					^ list3.FirstOrDefault().Age.GetHashCode();

	test.Dump("test");
	var list3list4CommonEquatable = list3.Intersect(list4);
	list3list4CommonEquatable.Dump("list3list4CommonEquatable");
	//Person.Equals(list4);
	
	
	Lambda.Dump("Lambda");

	var comparer = new PersonComparer();

	var commonPeople = list3.Intersect(list4, comparer);
	commonPeople.Dump("commonPeople");
	//foreach (var item in list3list4CommonEquatable)
	//{
	//	//item.Name.Dump();
	//	Console.WriteLine(item.Name); // Bob
	//}
}
Posted
Updated 5-Sep-24 21:26pm
v3

1 solution

To answer the first part of your question, the HashCode is creating a exclusive or (XOR) of the two values to create a unique hash. This is something you often see as a common way of creating a hash; the idea of XORing data together is to make random values even more random.

When you create a comparer, you are creating it in the expectation that it is going to be passed into something that expects to compare multpile "things". In other words, you have extracted the logic of determining whether two items are the same away from those items, and into an external object. I appreciate that doesn't seem clear, but bear with me for a moment.

In PersonComparer, the Equals method is saying that two objects are the same only if the Name and Age match. The more properties you have, the more you add to PersonComparer. Now, because the logic for checking whether the items are the same doesn't sit inside the Person class, you actually have to pass PersonComparer into the operation that needs to determine whether two objects match or not. This is why you see the issue with the Intersect at first; when you don't pass the comparer in, it applies a default equality check which isn't going to return what you're expecting. As soon as you add the comparer in, the intersection can be correctly determined as only those records where the Name and Age match.

If you don't want to use an external comparer, you could always override the Equals method on the Person object. Equals is a overrideable method on every object so you could do the same logic without the comparer. What is interesting to note is that overriding the equality operation only impacts codee that calls the Equals method. Anything that uses == is going to miss out the equality check unless you override the == operator as well.
 
Share this answer
 
Comments
Aggeliki Asimakopoulou 6-Sep-24 7:59am    
Thank you so much, I would like to make some additional questions please.
About HashCode, I read somewhere that is a int32 number how do we know that we will not have any overflow of int32 limit when we XORing data together?

You said that I could always override the Equals on the Person object instead of using external comparer. Is there a way to override == operator and how? May you give an example please?
I tried to override the Equals, but because we compare lists of person I thought it should be public class Person : IEquatable<list<person>> but it's not right because we compare person to another person through IEquatable, so it looks a little bit confused. Does overriden operator serve that functionallity?
Pete O'Hanlon 6-Sep-24 8:34am    
First of all, it should be public class Person : IEquatable<person>. You're equating one person to another not a list of people. To override the == operator, just use something like this:

public static bool operator ==(Person lhs, Person rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}

return false;
}
return lhs.Equals(rhs);
}
Aggeliki Asimakopoulou 6-Sep-24 9:48am    
Thank you so much.
I think I got it, when we override the == operator for example we need to override != too

public static bool operator ==(Person lhs, Person rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}

return false;
}
return lhs.Equals(rhs);
}

public static bool operator !=(Person lhs, Person rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}

return false;
}
return !lhs.Equals(rhs);
}

However, are we able to override operators inside Iequitable class or should we do it inside a simple class?
Pete O'Hanlon 6-Sep-24 10:10am    
Overriding operators has to be done inside the class.
Aggeliki Asimakopoulou 6-Sep-24 10:29am    
Thank you so much!!! It works and I understand it.

public class Person //: IEquatable<person>
{
public string Name { get; set; }
public int Age { get; set; }

//with override Equals it acts like IEquatable<person> without the need to declare it.


public override bool Equals(object o)
{
if (o is Person student)
{
return (this.Name == student.Name && this.Age == student.Age);
}
return false;
//throw new NotImplementedException();
}

//if I use : IEquatable<person> I don't use override but I am getting warning
//user query Person defines operator == and != but doesn't override Equals (object o)
//however, it works!
//public bool Equals(Person other)
//{
// if (other is Person student)
// {
// return (this.Name == student.Name && this.Age == student.Age);
// }
// return false;
//}


public override int GetHashCode()
{
return this.Name.GetHashCode() ^ this.Age.GetHashCode();
}
//public static bool operator ==(Person left, Person right)
//{
// if (ReferenceEquals(left, null))
// {
// return ReferenceEquals(right, null);
// }
// return left.Equals(right);
//}
//
//public static bool operator !=(Person left, Person right)
//{
// return !(left == right);
//}

public static bool operator ==(Person lhs, Person rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}

return false;
}
return lhs.Equals(rhs);
}

public static bool operator !=(Person lhs, Person rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}

return false;
}
return !lhs.Equals(rhs);
}


}

void Main()
{
var list3 = new List<person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 },
new Person { Name = "Charlie", Age = 35 },
new Person { Name = "Eve", Age = 50 }
};

var list4 = new List<person>
{
new Person { Name = "Bob", Age = 30 },
new Person { Name = "David", Age = 35 },
new Person { Name = "Eve", Age = 50}//40 }
};

list3.Dump("list3");
list4.Dump("list4");

list3.Intersect(list4).Dump("intersection");

list3.Except(list4).Dump("exception");
}

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900