C# – Why can’t the operator ‘==’ be applied to a struct and default(struct)

coperatorsstruct

I'm seeing some odd behaviour after using FirstOrDefault() on a collection of structs. I've isolated it into this reproduction case. This program won't compile

using System;
using System.Linq;

namespace MyProgram {
    public class Program {
        static void Main() {

            var users = new User[] {
            new User() { UserGuid = Guid.NewGuid(), Username = "user01" },
            new User() { UserGuid = Guid.NewGuid(), Username = "user02" }
        };

            var user = users.FirstOrDefault(u => u.Username == "user01");
            Console.WriteLine(user == default(User) ? "not found" : "found");
        }

    }

    public struct User {
        public Guid UserGuid;
        public string Username;
    }
}

The compiler error is the rather cryptic:

Operator '==' cannot be applied to operands of type 'MyProgram.User' and 'MyProgram.User'

Changing the struct to a class works fine – but I'm at a loss as to why I can't compare a struct 'instance' to a default?

Best Answer

For classes, the == operator uses reference equality. Of course, structs are value types, so they can't be compared by reference. There is no default implementation of == for structs because memberwise comparison isn't always a valid comparison, depending on the type.

You can instead use the Object.Equals method, which does compare memberwise:

Console.WriteLine(user.Equals(default(User)) ? "not found" : "found");

Or you could just implement == to call Object.Equals:

public static bool operator ==(User lhs, User rhs)
{
    return lhs.Equals(rhs);
}

However, the default implementation of Equals for structs uses reflection, and so is very slow. It would be better to implement Equals yourself, along with == and != (and possibly GetHashCode too):

public override bool Equals(Object obj)
{
    return obj is User && Equals((User)obj);
}

public bool Equals(User other)
{
    return UserGuid == other.UserGuid && Username == other.Username;
}

public static bool operator ==(User lhs, User rhs)
{
    return lhs.Equals(rhs);
}

public static bool operator !=(User lhs, User rhs)
{
    return !lhs.Equals(rhs);
}