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

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

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

С#:

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

Ответ 1

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

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

В принципе, операторы не работают одинаково и не должны работать одинаково.

Ответ 2

В дополнение к Jons ответ, который объясняет сторону С# вещей, понимает, что делает VB:

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

Фактически, с Option Strict On, VB ведет себя еще более строже, чем С#: в С#, 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 до нескольких лет назад, и Ive абсолютно не знаю, что a = b делает с Option Strict Off. Он делает какое-то сравнение равенства, но что именно происходит и почему, не знаю. Его более сложная, чем функция С# s dynamic, хотя (потому что это зависит от хорошо документированного API). Вот что говорит MSDN:

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

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

Ответ 3

Объектные экземпляры не сравниваются с оператором "==". Вы должны использовать метод "equals". С помощью оператора "==" сравниваются ссылки, а не объекты.

Попробуйте следующее:

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

Ответ 4

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

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

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

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

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