How does List<Struct>.Contains() work?

I have programed a very simple struct in C# (I'm using Visual Studio 2012 with .NET 4.5)

public struct MyStruct
{
    public Int32 X { get; set; }
    public Int32 Y { get; set; }

}

When I perform the following test to compare two different structs using equality operator ==:

private void MyTest()
{
    var sA = new MyStruct() { X = 1, Y = 2 };
    var sB = new MyStruct() { X = 10, Y = 20 };

    // Expected error occurs here.
    Console.WriteLine(sA == sB ? "true" : "false");

}

I get the expected error Operator '==' cannot be applied to operands of type 'MyStruct' and 'MyStruct'. Nothing new or interesting here. However, here's where things get weird. Let's say I try to do the following:

private void button1_Click(object sender, EventArgs e)
{
    var sA = new MyStruct() { X = 10, Y = 10 };
    var sB = new MyStruct() { X = 20, Y = 20 };
    var sC = new MyStruct() { X = 30, Y = 30 };
    var sD = new MyStruct() { X = 10, Y = 10 };

    // Note that struct sD contains the same data as sA.

    var myList = new List<MyStruct>();

    myList.Add(sA);
    myList.Add(sB);

    // This line writes "true" to the console window.
    Console.WriteLine(myList.Contains(sA) ? "true" : "false");

    // This line writes "true" to the console window.
    Console.WriteLine(myList.Contains(sB) ? "true" : "false");

    // This line writes "false" to the console window.
    Console.WriteLine(myList.Contains(sC) ? "true" : "false");

    // This line writes "true" to the console window.
    Console.WriteLine(myList.Contains(sD) ? "true" : "false");

}

How does the .Contains() method perform the comparison? It's clearly not comparing references since it indicates that sD is in the list. It seems like it's going into the structs and comparing them property by property (e.g. sA.X == sD.X and sA.Y == sD.Y). Does anyone know what is going on?

Jon Skeet
people
quotationmark

How does the .Contains() method perform the comparison?

It uses the default equality comparer for the type, as documented:

This method determines equality by using the default equality comparer

So essentially, something like this (but with null handling too):

var comparer = EqualityComparer<T>.Default;
foreach (var item in this)
{
    if (comparer.Equals(item, target))
    {
        return true;
    }
}
return false;

EqualityComparer<MyStruct>.Default will just perform a simple comparison on the fields, although I'd urge you to override Equals and implement IEquatable<T> on the struct yourself - as well as making it immutable.

people

See more on this question at Stackoverflow