Почему C # не сравнивает два типа объектов друг с другом, а VB - нет?

152

У меня есть два объекта в C #, и я не знаю, является ли он логическим или любого другого типа. Однако, когда я пытаюсь сравнить эти C # не дает правильный ответ. Я пробовал тот же код с VB.NET, и это было сделано!

Может кто-нибудь сказать мне, как это исправить, если есть решение?

C #:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True
Мохсен Саркар
источник
3
что если вы измените сравнение на равенство a.Equals(b)?
Джейсон Мекли,
8
Это хороший вопрос для педагогических целей.
Лобо
10
Потому что ваш код VB.NET не равен вашему коду C #.
Собака безопасности
9
При назначении aвы получаете бокс и создаете коробку, содержащую true. При назначении bвы получаете еще один ящик, содержащий true. Когда вы сравниваете aи b, поскольку оба имеют тип времени компиляции object, вы вызываете перегрузку, operator ==(object, object)определенную Спецификацией языка C #. Эта перегрузка проверяет, идут ли ссылки на один и тот же объект. Поскольку у вас есть два поля, результат будет false, и оператор «под» ваш ifне будет работать. Чтобы лучше это понять, попробуйте изменить назначение bна это: object b = a;теперь у вас есть только одна коробка.
Джеппе Стиг Нильсен
3
Раньше у меня был повод сказать: «Будьте осторожны, предполагая, что VB.NET и C # - это один и тот же язык, на котором говорят с разным акцентом - это не так»
AakashM

Ответы:

168

В C # ==оператор (применительно к выражениям ссылочного типа) выполняет проверку равенства ссылок, если только он не перегружен . Вы сравниваете две ссылки, которые являются результатом преобразования в бокс, так что это разные ссылки.

РЕДАКТИРОВАТЬ: С типами, которые перегружают ==, вы можете получить различное поведение - но это основано на типе выражений во время компиляции . Например, stringпредоставляет ==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

Здесь первое сравнение использует перегруженный оператор, а второе использует сравнение ссылок «по умолчанию».

В VB =оператор выполняет гораздо больше работы - это даже не просто эквивалентно использованию object.Equals(x, y), поскольку такие вещи Option Compareмогут влиять на сравнение текста.

По сути, операторы не работают одинаково и не должны работать одинаково.

Джон Скит
источник
17
+1 Я знал, что вы будете рядом, вы ЛЮБИТЕ такие загадочные вопросы :)
Абдусалам Бен Хадж
3
@AbZy: Я надеялся, что смогу дать более подробное объяснение того, что =сделал в VB, но спецификация не очень ясна.
Джон Скит
интересная вещь, но изменение объекта на динамическое ведет себя так же, как VB
VladL
4
@VladL: Да, потому что тогда он будет идти по типам времени выполнения и выполнять bool == boolсравнение.
Джон Скит
1
@Mahdi Lobo, возможно, предоставил код, но его ответ также неверен, в отличие от Джона.
Сервировка
79

В дополнение к ответу Джона, который объясняет сторону C #, вот что делает VB:

В VB с Option Strict Onпомощью сравнения = всегда проверяется равенство значений, а не равенство ссылок. Фактически, ваш код даже не компилируется после переключения, Option Strict Onпотому System.Objectчто не определяет Operator=. Вы должны всегда иметь эту опцию, она ловит ошибки более эффективно, чем мухоловка Венеры (хотя в вашем конкретном случае это слабое поведение на самом деле делает правильные вещи). 1

На самом деле, с помощью Option Strict OnVB ведет себя еще строже, чем в C #: в C # a == b либо инициирует вызов, SomeType.operator==(a, b)либо, если его нет, вызывает сравнение равенства ссылок (что эквивалентно вызовуobject.ReferenceEquals(a, b) ).

В VB, с другой стороны, сравнение a = b всегда вызывает оператор равенства. 2 Если вы хотите использовать сравнение равенства ссылок, вы должны использовать a Is b(что, опять же, так же, как Object.ReferenceEquals(a, b)).


1) Вот хорошее указание на то, почему использование Option Strict Offявляется плохой идеей: я использовал VB.NET почти десять лет, с момента официального выпуска .NET до нескольких лет назад, и я абсолютно не знаю, что a = bс этим делать Option Strict Off. Это делает какое-то сравнение на равенство, но что именно происходит и почему, понятия не имею. Однако это более сложно, чем dynamicфункция C # (потому что это опирается на хорошо документированный API). Вот что говорит MSDN:

Поскольку Option Strict Onобеспечивает строгую типизацию , предотвращает непреднамеренные преобразования типов с потерей данных, запрещает позднее связывание и повышает производительность, настоятельно рекомендуется его использование.

2) Джон упомянул одно исключение, строки, где сравнение на равенство делает еще кое-что из соображений обратной совместимости.

Конрад Рудольф
источник
4
+1. Я думаю, что это один из случаев, когда разработчикам VB.NET удалось заставить язык «просто работать» для программистов из VB6 и VBA, где ООП гораздо менее заметна, и поэтому концепция равенства ссылок гораздо менее важна. VB-кодировщик может написать хороший рабочий код, не задумываясь об объектах и ​​так далее.
Джон М Гант
5
+1 За это проголосовали не так, как хотелось бы. Неиспользование Option Strict Onдолжно рассматриваться как уголовное преступление ...
Охотник на оленей
1
@JohnMGant: кодировщик, который не понимает значимости ссылочной идентичности, может написать код, который работает, но вряд ли действительно знает, что можно безопасно изменить, какие изменения всегда сломают, а какие могут кажутся работающими, но вызывают нежелательные неприятные побочные эффекты (например, вызывая ссылки на разные изменяемые объекты, имеющие одинаковое состояние, вместо ссылок на один и тот же объект). Если объекты редко видоизменяются, такое изменение может не вызвать каких-либо непосредственных проблем, но может привести к появлению трудно обнаруживаемых ошибок позже.
суперкат
4

Экземпляры объекта не сравниваются с оператором "==". Вам следует использовать метод «равно». С оператором "==" сравниваются ссылки, а не объекты.

Попробуй это:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Полученные результаты:

a reference is not equal to b reference
a object is not equal to b object

Теперь попробуйте это:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Полученные результаты:

a reference is not equal to b reference
a object is equal to b object
лобо
источник
1
Это просто потому, что ты не переопределил operator ==. Если вы переопределите этот оператор и не равно, то ваш вывод будет обратным. Ничто не присуще сравнению ссылок operator ==и ничего не присуще сравнению значений в Equals. Это всего лишь два способа определения равенства; оба имеют стандартные реализации сравнения ссылок, и оба могут быть переопределены, чтобы делать то, что вы хотите, чтобы они делали. Единственное другое отличие состоит в том, что Equalsявляется виртуальным, а operator ==не является.
Сервировка
1
@Servy: обратите внимание, что вы не можете переопределить == - вы можете только перегрузить его.
Джон Скит
1
Извините, -1. Этот ответ просто неверен и не должен быть принятым.
Конрад Рудольф
Где-то есть вопрос Java, ожидающий этого ответа.
Чед Шуггинс,
3

Проблема в том, что оператор == в C # - это вызов статического метода (ну, может быть, не технически, но может быть как таковой), основанного на типе времени компиляции двух параметров. Каковы фактические типы времени выполнения этих объектов, не имеет значения.

На основе этого типа времени компиляции компилятор определит, какую реализацию operator ==использовать. Он может использовать objectреализацию по умолчанию , может использовать одну из числовых перегрузок, предоставляемых языком, или это может быть реализация, определяемая пользователем.

Это отличается от VB тем, что VB не определяет реализацию во время компиляции. Он ожидает выполнения и проверяет два заданных параметра, чтобы определить, какую реализацию ==оператора он должен использовать.

Ваш код содержит логические значения, но они находятся в переменных, которые имеют тип object. Поскольку переменная имеет тип object, компилятор C # использует objectреализацию ==, которая сравнивает ссылки , а не экземпляры объекта. Поскольку логические значения являются блоками, они не имеют одинаковых ссылок, даже если их значения одинаковы.

Код VB не имеет значения, какой тип переменной. Он ждет до времени выполнения, а затем проверяет две переменные, видит, что они на самом деле имеют тип boolean, и поэтому использует реализацию логического ==оператора. Эта реализация сравнивает значения логических значений, а не их ссылок (и логические значения будут распакованы перед вызовом этого оператора, поэтому сравнение ссылок даже не имеет смысла). Поскольку значения логических значений совпадают, возвращается значение true.

Servy
источник
Это выглядит хорошо для C #; Я не знаю достаточно точно о том, что =делает в VB, чтобы сказать наверняка.
Джон Скит
@JonSkeet Справедливо достаточно.
Сервировка
Согласно msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , в разделе «Программирование без типов с операторами реляционного сравнения»: =вместе со всеми другими операторами реляционного сравнения, такими как <, >=и т. Д. , получают специальную обработку, когда обе стороны или одна из сторон оператора Object. Эта специальная обработка сделана так, чтобы программисты VB6, которые привыкли использовать тип, известный как до Variant.NET VB, могли использовать Objectв VB.Net способами, которые они использовали Variantпрежде.
rskar
Другими словами, оставляя в стороне эффекты перегрузки и Option Strict On, VB =смещен в сторону распаковки Objectдо тех пор, пока не сможет получить строку или число.
rskar