Reputation: 93
Newbie here. I read the following code at Microsoft docs. You can see the webpage here: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.find?view=net-6.0
public class Part : IEquatable<Part>
{
public string PartName { get; set; }
public int PartId { get; set; }
//other code
public override bool Equals(object obj)
{
if (obj == null) return false;
Part objAsPart = obj as Part;
if (objAsPart == null) return false;
else return Equals(objAsPart);
}
// other code
public bool Equals(Part other)
{
if (other == null) return false;
return (this.PartId.Equals(other.PartId));
}
}
public class Example
{
public static void Main()
{
// Create a list of parts.
List<Part> parts = new List<Part>();
// Add parts to the list.
parts.Add(new Part() { PartName = "crank arm", PartId = 1234 });
parts.Add(new Part() { PartName = "chain ring", PartId = 1334 });
parts.Add(new Part() { PartName = "regular seat", PartId = 1434 });
parts.Add(new Part() { PartName = "banana seat", PartId = 1444 });
parts.Add(new Part() { PartName = "cassette", PartId = 1534 });
parts.Add(new Part() { PartName = "shift lever", PartId = 1634 }); ;
//other code
// Check the list for part #1734. This calls the IEquatable.Equals method
// of the Part class, which checks the PartId for equality.
Console.WriteLine("\nContains: Part with Id=1734: {0}",
parts.Contains(new Part { PartId = 1734, PartName = "" }));
}
}
Now, my question is about parts.Contains(new Part { PartId = 1734, PartName = "" })
line.
This is the ListT.Contains(T)
method.
As you can see from the Microsoft reference source (here: https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,521b9f7129105e15), the code for Contains
method is the following:
public bool Contains(T item) {
if ((Object) item == null) {
for(int i=0; i<_size; i++)
if ((Object) _items[i] == null)
return true;
return false;
}
else {
EqualityComparer<T> c = EqualityComparer<T>.Default;
for(int i=0; i<_size; i++) {
if (c.Equals(_items[i], item)) return true;
}
return false;
}
}
So as you can see Contains
method uses Equals
method, but which Equals
of all?
c.Equals(_items[i], item)
means that the method we are calling belongs to EqualityComparer<T>.Default
.
Indeed Default
is a property of EqualityComparer<T>
class and it returns an object of the same class.
So the Equals
we see here should belong to the EqualityComparer<T>
class.
Question #1: How can we have an object of EqualityComparer<T>
class since this class is abstract?
Question #2: How can we call c.Equals(_items[i], item)
since this method is also abstract? (as you can see here: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.equalitycomparer-1.equals?view=net-6.0
But the most important of all,
Question #3: How do we go from c.Equals(_items[i], item)
method of class EqualityComparer<T>
, to the IEquatable.Equals
method which is implemented in the Part
class. Does the first method invoke the second?
Here: https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.equalitycomparer-1?view=net-6.0 it says the following:
"The
Default
property checks whether typeT
implements theSystem.IEquatable<T>
generic interface and, if so, returns anEqualityComparer<T>
that invokes the implementation of theIEquatableT.Equals
method. Otherwise, it returns anEqualityComparer<T>
, as provided byT
."
I don't understand how the second method, IEquatable.Equals
, is invoked.
I am sorry for the long post. Thanks in advance!
Upvotes: 5
Views: 221
Reputation: 142943
How can we have an object of
EqualityComparer<T>
class since this class is abstract?
You don't, you have an instance of some inheritor of EqualityComparer<T>
created in the EqualityComparer<T>.CreateComparer
method.
Question #2: How can we call
c.Equals(_items[i], item)
since this method is also abstract?
Because concrete inheritor of EqualityComparer<T>
should and implements this method (read the doc about abstract
keyword). Slightly modified example from the doc may clarify some things:
// create an instance of Square and save to variable of Square type:
Square square = new Square(12);
// assign Square to variable of Shape type, works because Square is Shape:
Shape shape = square;
// access the abstract method declared on Shape and implemented by Square:
Console.WriteLine($"Area of the square = {shape.GetArea()}");
abstract class Shape
{
public abstract int GetArea();
}
class Square : Shape
{
private int _side;
public Square(int n) => _side = n;
// GetArea method is required to avoid a compile-time error.
public override int GetArea() => _side * _side;
}
Question #3: How do we go from
c.Equals(_items[i], item)
method of classEqualityComparer<T>
, to theIEquatable.Equals
method which is implemented in the Part class. Does the first method invoke the second?
Because already mentioned CreateComparer
method checks if generic type T
implements this interface and if it does - uses the implementation in the Default
instance:
// If T implements IEquatable<T> return a GenericEqualityComparer<T>
if (typeof(IEquatable<T>).IsAssignableFrom(t)) {
return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), t);
}
Few extra notes:
Upvotes: 5