Search code examples
c#structoperators

Why can't the operator '==' be applied to a struct and default(struct)?


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?


Solution

  • 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);
    }