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