Почему оператор '==' не может применяться к структуре и по умолчанию (struct)?
Я вижу некоторое нечетное поведение после использования FirstOrDefault() в коллекции структур. Я изолировал его в этом случае. Эта программа не будет компилировать
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;
}
}
Ошибка компилятора является довольно загадочной:
Оператор '==' не может применяться к операндам типа "MyProgram.User" и "MyProgram.User"
Изменение структуры для класса отлично работает, но я не понимаю, почему я не могу сравнить экземпляр struct с экземпляром по умолчанию?
Ответы
Ответ 1
Для классов оператор ==
использует ссылочное равенство. Конечно, структуры - это типы значений, поэтому их нельзя сравнивать по ссылке. Для структур нет реализации по умолчанию ==
, потому что сравнение пополам не всегда является допустимым сравнением, в зависимости от типа.
Вместо этого вы можете использовать метод Object.Equals
, который выполняет сравнение по порядку:
Console.WriteLine(user.Equals(default(User)) ? "not found" : "found");
Или вы могли бы просто реализовать ==
для вызова Object.Equals
:
public static bool operator ==(User lhs, User rhs)
{
return lhs.Equals(rhs);
}
Однако реализация по умолчанию Equals
для структур использует отражение, и поэтому происходит очень медленно. Было бы лучше реализовать Equals
самостоятельно, а также ==
и !=
(и, возможно, GetHashCode
):
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);
}
Ответ 2
Вам просто нужно его реализовать:
public static bool operator == (User u1, User u2)
{
return u1.Equals(u2); // use ValueType.Equals() which compares field-by-field.
}
Ответ 3
В С# токен ==
используется для представления двух разных операторов (не все языки используют один и тот же токен для двух операторов, VB.NET использует токены =
и Is
). Один из операторов является перегружаемым тестом на равенство и доступен только в случаях, когда для обоих типов операндов определяется либо перегрузка, либо перегрузка определена для одного типа операнда и типа, к которому другой операнд неявно конвертируется. Другой оператор представляет собой критерий равенства ссылок и может использоваться в случаях, когда оператор проверки равенства будет непригодным, а один операнд - это тип класса, который происходит от другого, один операнд - тип класса, а другой - тип интерфейса или оба операнда являются типами интерфейсов.
Первый оператор проверки равенства не может использоваться с любым типом (классом, интерфейсом или структурой), который не предоставляет явного переопределения для него. Если токен ==
используется в случаях, когда первый оператор проверки равенства не используется, С# будет пытаться использовать второй оператор [обратите внимание, что другие языки, такие как VB.NET, не будут этого делать; в VB.NET попытка использовать =
для сравнения двух вещей, которые не определяют перегрузку проверки равенства, будет ошибкой, даже если вещи можно было бы сравнить с помощью оператора Is
]. Этот второй оператор может использоваться для сравнения любого ссылочного типа с другой ссылкой того же типа, но не может использоваться со структурами. Поскольку ни один тип оператора равенства не определен для структур, сравнение не разрешено.
Если вам интересно, почему ==
не просто отбрасывается на Equals(Object)
, который можно использовать для всех типов, причина в том, что оба операнда ==
подвержены типу принуждения способами, которые предотвратили бы его поведение от соответствия Equals
. Например, 1.0f == 1.0 и 1.0 == 1.0f, оба передают операнд float
на double
, но с учетом выражения типа (1.0f).Equals(1.0)
первый операнд не может быть оценен как что-либо, кроме float
, Кроме того, если ==
были сопоставлены с Equals
, тогда было бы необходимо, чтобы С# использовал другой токен для представления теста ссылочного равенства [то, что язык должен был сделать в любом случае, но, по-видимому, не хотел этого делать ].
Ответ 4
Вы можете перегрузить оператор ==
, если вы хотите сделать это
public static bool operator ==(User u1, User u2)
{
return u1.Equals(u2)
}
Вы также должны переопределить Equals
и GetHashCode()
Также, если вы переопределите ==
, вы, вероятно, захотите также переопределить !=
.
public static bool operator !=(User u1, User u2)
{
return !u1.Equals(u2)
}
Ответ 5
Когда вы сравниваете два типа ссылок, вы проверяете, ссылаются ли ссылки на один и тот же тип.
Но если вы имеете дело со типами значений, ссылок на сравнение нет.
Вам нужно реализовать оператор самостоятельно и (возможно) проверить, соответствуют ли поля типа значений.