Чому 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;Тепер у вас є лише одне поле.
Jeppe Stig Nielsen

3
Раніше я мав нагоду сказати "Будьте уважні, припускаючи, що VB.NET і C # - це одна і та ж мова, якою розмовляють з іншим акцентом - їх немає"
AakashM

Відповіді:


168

У C #, ==оператор (якщо застосовується до виразів опорного типу) здійснює перевірку рівності еталонних даних, якщо він не перевантажений . Ви порівнюєте дві посилання, які є результатом перетворень боксу, тому це окремі посилання.

EDIT: За типи, які перевантажують ==, ви можете отримувати різну поведінку, але це базується на типі виразів часу компіляції . Наприклад, 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
@ Махді Лобо, можливо, надав код, але його відповідь також неправильна, на відміну від Джона.
Сервіс

79

Окрім відповіді Джона, яка пояснює C # сторону речей, ось що робить VB:

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

Насправді, з Option Strict On, VB поводиться навіть суворіше, ніж 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, де OOP набагато менш помітний, і тому концепція рівності референцій набагато менш важлива. Кодер VB може написати хороший робочий код, не замислюючись про об'єкти тощо.
Джон М Гант

5
+1 Це не сприймається настільки, як насправді повинно. Не використання Option Strict Onповинно вважатися злочинним злочином ...
Мисливець на оленів

1
@JohnMGant: Кодер, який не розуміє значення опорної ідентичності, може бути в змозі написати код, який трапляється в роботі, але навряд чи дійсно знає, які речі можна безпечно змінити, які зміни завжди будуть ламати речі, і які зміни можуть бути начебто спрацьовують, але викликають небажані неприємні побічні ефекти (наприклад, спричинення того, що має бути посиланням на різні об'єкти, що змінюються, які мають той самий стан, натомість посилання на один і той же об’єкт). Якщо об'єкти рідко мутуються, така зміна може не викликати негайних проблем, але може змусити пізніше з’явитись важко знайти помилок.
supercat

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 не має значення, який тип змінної. Він чекає, поки час виконання, а потім перевіряє дві змінні, бачить, що вони насправді є обома булевими типами, і тому використовує булевий== оператора. Ця реалізація порівнює значення булевих, а не їх посилань (і булеві будуть виводитись з коду до виклику цього оператора, тому порівняння посилань навіть більше не має сенсу). Оскільки значення булевих значень однакові, воно повертає істинні.


Це добре виглядає для C #; Я не знаю достатньо точно про те, що точно =робить VB, щоб точно сказати.
Джон Скіт

@JonSkeet Ярмарок досить.
Сервіс

Per msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , в розділі «безтипних Програмування з реляційними операторами порівняння»: =поряд з усіма іншими реляційними операторами порівняння , такими як <, >=і т.д. , отримують спеціальну обробку, коли сторона або оператор або обидва боки є Object. Ця спеціальна обробка робиться так, що програмісти VB6, які звикли використовувати тип, відомий як Variantу pre-.NET VB, можуть використовувати ObjectVB.Net способами, якими вони користувалися Variantраніше.
rskar

Якщо говорити іншим способом, і не відмовляючись від ефектів перевантаження та Option Strict On, VB =схиляється до розпакування, Objectпоки не зможе дістатись до String або числових.
rskar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.