Согласно документации ==
оператора в MSDN ,
Для предопределенных типов значений оператор равенства (==) возвращает true, если значения его операндов равны, в противном случае - false. Для ссылочных типов, отличных от string, == возвращает true, если два его операнда ссылаются на один и тот же объект. Для типа строки == сравнивает значения строк. Пользовательские типы значений могут перегружать оператор == (см. Оператор). То же самое можно сказать и о пользовательских ссылочных типах, хотя по умолчанию == ведет себя так, как описано выше, как для предопределенных, так и для пользовательских ссылочных типов.
Так почему этот фрагмент кода не компилируется?
bool Compare<T>(T x, T y) { return x == y; }
Я получаю сообщение об ошибке. Оператор '==' не может быть применен к операндам типа 'T' и 'T' . Интересно почему, поскольку, насколько я понимаю, ==
оператор предопределен для всех типов?
Редактировать: Спасибо всем. Сначала я не заметил, что утверждение касается только ссылочных типов. Я также подумал, что побитовое сравнение предоставляется для всех типов значений, что, как я теперь знаю, не является правильным.
Но если я использую ссылочный тип, будет ли ==
оператор использовать предопределенное сравнение ссылок, или он будет использовать перегруженную версию оператора, если тип определен?
Редактировать 2: методом проб и ошибок мы узнали, что ==
оператор будет использовать предопределенное эталонное сравнение при использовании неограниченного универсального типа. Фактически, компилятор будет использовать лучший метод, который он может найти для аргумента ограниченного типа, но не будет искать дальше. Например, приведенный ниже код будет всегда печататься true
, даже когда Test.test<B>(new B(), new B())
вызывается:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
источник
==
нельзя использовать два операнда одного и того же типа. Это верно дляstruct
типов (кроме «предопределенных» типов), которые не перегружаютoperator ==
. В качестве простого примера попробуйте следующее:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, тогда вы не можете проверить,kvp1 == kvp2
потому чтоKeyValuePair<,>
это структура, это не предопределенный тип C #, и он не перегружаетoperator ==
. Тем не менее, приведен пример,var li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
с которым вы не можете справитьсяe1 == e2
(здесь у нас есть вложенная структураList<>.Enumerator
("List`1+Enumerator[T]"
вызывается средой выполнения), которая не перегружается==
).bool
void
Ответы:
«... по умолчанию == ведет себя так, как описано выше как для предопределенных, так и для пользовательских типов ссылок.»
Тип T не обязательно является ссылочным типом, поэтому компилятор не может сделать это предположение.
Тем не менее, это скомпилируется, потому что это более явно:
Ответьте на дополнительный вопрос: «Но если я использую ссылочный тип, будет ли оператор == использовать предопределенное сравнение ссылок или будет перегруженная версия оператора, если тип определен?»
Я бы подумал, что == на Generics будет использовать перегруженную версию, но следующий тест демонстрирует обратное. Интересно ... Я хотел бы знать, почему! Если кто-то знает, пожалуйста, поделитесь.
Вывод
Встроенный: перегружен == называется
Общий:
Нажмите любую клавишу для продолжения . , ,
Follow Up 2
Я хочу отметить, что изменение моего метода сравнения на
вызывает перегруженный оператор ==. Я предполагаю, что без указания типа (как где ) компилятор не может сделать вывод, что он должен использовать перегруженный оператор ... хотя я думаю, что у него будет достаточно информации, чтобы принять это решение, даже не указав тип.
источник
Equals
метода (а не в==
операторе).==
между универсальными типамиT
иT
, наилучшая перегрузка найдена, учитывая, какие ограничения переносятсяT
(есть специальное правило, что он никогда не будет помечать тип значения для этого (который дал бы бессмысленный результат), следовательно, должно быть некоторое ограничение, гарантирующее, что это ссылочный тип). В вашем Follow Up 2 , если вы входите сDerivedTest
объектами иDerivedTest
производите,Test
но==
вводите новую перегрузку , у вас снова будет «проблема». Какая перегрузка называется, «сгорает» в IL во время компиляции.Как уже говорили другие, он будет работать только тогда, когда T ограничен ссылочным типом. Без каких-либо ограничений вы можете сравнивать со значением NULL, но только со значением NULL - и это сравнение всегда будет ложным для типов значений, не допускающих значения NULL.
Вместо вызова Equals лучше использовать
IComparer<T>
- и если у вас нет больше информации,EqualityComparer<T>.Default
это хороший выбор:Помимо всего прочего, это позволяет избежать бокса / кастинга.
источник
В общем,
EqualityComparer<T>.Default.Equals
следует выполнять работу с чем-либо, что реализуетIEquatable<T>
или имеет разумнуюEquals
реализацию.Если, однако,
==
иEquals
по какой-то причине реализованы по-другому, то моя работа над универсальными операторами должна быть полезной; поддерживает операторские версии (среди прочих):источник
Так много ответов, и ни один не объясняет ПОЧЕМУ? (который явно спросил Джованни) ...
Обобщения .NET не действуют как шаблоны C ++. В шаблонах C ++ разрешение перегрузки происходит после того, как фактические параметры шаблона известны.
В универсальных .NET (в том числе C #) разрешение перегрузки происходит без знания фактических универсальных параметров. Единственная информация, которую компилятор может использовать для выбора вызываемой функции, связана с ограничениями типов общих параметров.
источник
==
работает для всех типов, будь то ссылочные типы или типы значений. Это должен быть вопрос, на который я не думаю, что вы ответили.==
не работает для всех типов значений. Что еще более важно, он не имеет одинакового значения для всех типов, поэтому компилятор не знает, что с ним делать.==
. Можете ли вы включить эту часть в свой ответ, так как я думаю, что это главное здесьКомпиляция не может знать, что T не может быть структурой (типом значения). Так что вы должны сказать, что это может быть только ссылочный тип, я думаю:
Это потому, что если T может быть типом значения, могут быть случаи, когда
x == y
он будет плохо сформирован - в тех случаях, когда для типа не определен оператор ==. То же самое произойдет для этого, что более очевидно:Это тоже не помогает, потому что вы можете передать тип T, у которого не будет функции foo. C # заставляет вас убедиться, что все возможные типы всегда имеют функцию foo. Это делается с помощью предложения where.
источник
Похоже, что без ограничения класса:
Следует понимать, что в то время как
class
ограничениеEquals
в==
операторе наследуется отObject.Equals
, а у структуры переопределяетсяValueType.Equals
.Обратите внимание, что:
также выдает ту же ошибку компилятора.
Пока я не понимаю, почему компилятор отвергает сравнение операторов равенства типов значений. Я точно знаю, что это работает:
источник
Object.Equals
но вместо этого проверяет равенство ссылок. Например,Compare("0", 0.ToString())
вернет false, поскольку аргументы будут ссылками на разные строки, каждая из которых имеет ноль в качестве единственного символа.NullReferenceException
может случиться.Ну, в моем случае я хотел провести модульное тестирование оператора равенства. Мне нужно было вызывать код под операторами равенства без явной установки универсального типа. Советы для
EqualityComparer
не были полезны какEqualityComparer
вызываемыйEquals
метод, но не оператор равенства.Вот как у меня это работает с универсальными типами путем построения
LINQ
. Он называет правильный код==
и!=
операторов:источник
Существует запись MSDN Connect для этого здесь
Ответ Алекса Тернера начинается с:
источник
Если вы хотите убедиться, что операторы вашего пользовательского типа вызываются, вы можете сделать это с помощью отражения. Просто получите тип, используя ваш универсальный параметр, и получите MethodInfo для нужного оператора (например, op_Equality, op_Inequality, op_LessThan ...).
Затем выполните оператор, используя метод Invoke для MethodInfo, и передайте объекты в качестве параметров.
Это вызовет ваш перегруженный оператор, а не тот, который определен ограничениями, примененными к универсальному параметру. Может быть непрактичным, но может пригодиться для модульного тестирования ваших операторов при использовании базового базового класса, который содержит пару тестов.
источник
Я написал следующую функцию, глядя на последний MSDN. Он может легко сравнить два объекта
x
иy
:источник
return ((IComparable)(x)).CompareTo(y) <= 0;
Вышесказанное будет работать, потому что == позаботится в случае пользовательских типов ссылок.
В случае типов значений == может быть переопределено. В этом случае "! =" Также должно быть определено.
Я думаю, что это может быть причиной, это запрещает общее сравнение, используя "==".
источник
==
Маркер используется для двух разных операторов. Если для данных типов операндов существует совместимая перегрузка оператора равенства, эта перегрузка будет использоваться. В противном случае, если оба операнда являются ссылочными типами, совместимыми друг с другом, будет использовано сравнение ссылок. Обратите внимание, что в приведенномCompare
выше методе компилятор не может сказать, что первое значение применимо, но может сказать, что второе значение применимо, поэтому==
токен будет использовать последнее, даже еслиT
перегружает оператор проверки равенства (например, если он имеет типString
) ..Equals()
Работает для меня в то время какTKey
это общий тип.источник
x.Id.Equals
не такid.Equals
. Предположительно, компилятор знает что-то о типеx
.