¿Por qué C # no puede comparar dos tipos de objetos entre sí, pero VB no?

152

Tengo dos objetos en C # y no sé si es booleano o de cualquier otro tipo. Sin embargo, cuando trato de comparar esos C # no puede dar la respuesta correcta. He intentado el mismo código con VB.NET y ¡lo hice!

¿Alguien puede decirme cómo solucionar esto si hay una solución?

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
Mohsen Sarkar
fuente
3
¿Qué pasa si cambia el comparador de igualdad a a.Equals(b)?
Jason Meckley
8
Esta es una buena pregunta para fines pedagógicos.
Lobo
10
Porque su código VB.NET no es igual a su código C #.
Sabueso de seguridad
9
Cuando lo asigna a, obtiene boxeo y crea un cuadro que contiene true. Cuando lo asigna b, obtiene otro cuadro que también contiene true. Cuando compara ay b, dado que ambos son del tipo de tiempo de compilación object, llama a la sobrecarga operator ==(object, object)definida por la Especificación del lenguaje C #. Esta sobrecarga verifica si las referencias van al mismo objeto. Como tiene dos cuadros, el resultado es false, y la declaración "debajo" ifno se ejecutará. Para comprender esto mejor, intente cambiar la asignación de ba esto: object b = a;ahora solo tiene una casilla.
Jeppe Stig Nielsen
3
He tenido la ocasión antes de decir "Tenga cuidado asumiendo que VB.NET y C # son el mismo idioma que se habla con un acento diferente, no lo son"
AakashM

Respuestas:

168

En C #, el ==operador (cuando se aplica a expresiones de tipo de referencia) realiza una verificación de igualdad de referencia a menos que esté sobrecargado . Estás comparando dos referencias que son el resultado de conversiones de boxeo, por lo que esas son referencias distintas.

EDITAR: con tipos que sobrecargan ==, puede obtener un comportamiento diferente, pero eso se basa en el tipo de tiempo de compilación de las expresiones. Por ejemplo, stringproporciona ==(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

Aquí, la primera comparación usa el operador sobrecargado, pero la segunda usa la comparación de referencia "predeterminada".

En VB, el =operador hace mucho más trabajo: ni siquiera es equivalente al uso object.Equals(x, y), ya que cosas como Option Comparepueden afectar la forma en que se compara el texto.

Básicamente, los operadores no funcionan de la misma manera y no están destinados a funcionar de la misma manera.

Jon Skeet
fuente
17
+1 Sabía que
estarías
3
@AbZy: esperaba poder proporcionar una explicación más detallada de lo que =hizo en VB, pero la especificación no es muy clara.
Jon Skeet
algo interesante, pero cambiar el objeto a dinámico se comporta igual que VB
VladL
44
@VladL: Sí, porque luego irá por los tipos de tiempo de ejecución y realizará la bool == boolcomparación.
Jon Skeet
1
@Mahdi Lobo puede haber proporcionado el código, pero su respuesta también es incorrecta, a diferencia de la de Jon.
Servy
79

Además de la respuesta de Jon que explica el lado C # de las cosas, esto es lo que hace VB:

En VB con Option Strict On, una comparación a través de = siempre prueba la igualdad de valores y nunca la igualdad de referencia. De hecho, su código ni siquiera se compila una vez que cambia Option Strict Onporque System.Objectno define un Operator=. Usted debe siempre tener esta opción, atrapa insectos con más eficacia que una venus atrapamoscas (aunque en su caso particular, este comportamiento laxa en realidad hace lo correcto). 1

De hecho, con Option Strict On, VB se comporta aún más estricto que C #: en C #, a == b ya sea activa una llamada a SomeType.operator==(a, b)o, si esto no existe, invoca comparación de igualdad de referencia (que es equivalente a llamarobject.ReferenceEquals(a, b) ).

En VB, por otro lado, la comparación a = b siempre invoca al operador de igualdad. 2 Si desea utilizar la comparación de igualdad de referencia, debe utilizarla a Is b(que, una vez más, es lo mismo que Object.ReferenceEquals(a, b))


1) Aquí hay una buena indicación de por qué usar Option Strict Offes una mala idea: he usado VB.NET durante casi una década, desde antes del lanzamiento oficial de .NET hasta hace unos años, y no tengo ni idea de qué a = bhace Option Strict Off. Hace algún tipo de comparación de igualdad, pero qué sucede exactamente y por qué, ni idea. Sin dynamicembargo, es más complejo que la función de C # (porque se basa en una API bien documentada). Esto es lo que dice el MSDN:

Debido a que Option Strict Onproporciona un tipeo fuerte , evita conversiones de tipos no deseadas con pérdida de datos, no permite el enlace tardío y mejora el rendimiento, se recomienda encarecidamente su uso.

2) Jon ha mencionado una excepción, cadenas, donde la comparación de igualdad hace algunas cosas más por razones de compatibilidad con versiones anteriores.

Konrad Rudolph
fuente
44
+1. Creo que este es un caso en el que los diseñadores de VB.NET lograron que el lenguaje "simplemente funcione" para los programadores que vienen de VB6 y VBA, donde OOP es mucho menos prominente y, por lo tanto, el concepto de igualdad de referencia es mucho menos importante. Un codificador VB puede escribir un buen código de trabajo sin pensar mucho en los objetos, etc.
John M Gant
55
+1 Esto no ha votado tanto como debería. No usar Option Strict Ontiene que ser considerado un delito penal ...
Deer Hunter
1
@JohnMGant: un codificador que no comprende la importancia de la identidad de referencia puede escribir código que funcione, pero es poco probable que sepa qué cosas se pueden cambiar con seguridad, qué cambios siempre romperán las cosas y qué cambios pueden parece funcionar pero causa efectos secundarios desagradables no deseados (p. ej., lo que debería ser referencias a diferentes objetos mutables que tienen el mismo estado en lugar de ser referencias al mismo objeto). Si los objetos rara vez están mutados, dicho cambio puede no causar problemas inmediatos, pero puede hacer que los errores difíciles de encontrar surjan más tarde.
supercat
4

Las instancias de objeto no se comparan con el operador "==". Debe usar el método "igual". Con el operador "==" se comparan referencias, no objetos.

Prueba esto:

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");
}

Resultados:

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

Ahora, intenta esto:

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");
}

Resultados:

a reference is not equal to b reference
a object is equal to b object
Lobo
fuente
1
Esto es simplemente porque no anulaste operator ==. Si anula ese operador y no es igual, su salida se revertirá. No hay nada inherente en comparar referencias operator ==ni nada inherente en comparar valores en Equals. Son solo dos formas de determinar la igualdad; ambos tienen implementaciones predeterminadas de una comparación de referencia, y ambos pueden anularse para hacer lo que usted quiera que hagan. La única otra diferencia es que Equalses virtual y operator ==no lo es.
Servy
1
@Servy: tenga en cuenta que no puede anular == , solo puede sobrecargarlo .
Jon Skeet
1
Lo siento, -1. Esta respuesta es simplemente incorrecta y no debería ser la aceptada.
Konrad Rudolph
En algún lugar hay una pregunta de Java esperando esta respuesta.
Chad Schouggins
3

El problema es que el operador == en C # es una llamada a un método estático (bueno, tal vez no técnicamente, pero puede considerarse como tal) según el tipo de tiempo de compilación de los dos parámetros. Los tipos de tiempo de ejecución reales de esos objetos no importan.

Basado en ese tipo de tiempo de compilación, el compilador determinará qué implementación operator ==usar. Puede usar el predeterminadoobject implementación , podría usar una de las sobrecargas numéricas proporcionadas por el lenguaje, o podría ser una implementación definida por el usuario.

Esto es diferente de VB en que VB no determina la implementación en tiempo de compilación. Espera hasta el tiempo de ejecución e inspecciona los dos parámetros que se le dan para determinar qué implementación del ==operador debe usar.

Su código contiene valores booleanos, pero están en variables que son de tipo object. Porque la variable es de tipo object, el compilador de C # usa la objectimplementación de ==, que compara las referencias , no las instancias de objeto. Como los valores booleanos son cuadros, no tienen la misma referencia, aunque sus valores sean los mismos.

Al código VB no le importa de qué tipo es la variable. Espera hasta el tiempo de ejecución y luego verifica las dos variables, ve que en realidad son del tipo booleano y, por lo tanto, usa la ==implementación del operador booleano . Esa implementación compara los valores de los booleanos, no sus referencias (y los booleanos se desempaquetarán antes de llamar a ese operador, por lo que una comparación de referencia ya no tiene sentido). Debido a que los valores de los booleanos son los mismos, devuelve verdadero.

Servy
fuente
Eso se ve bien para el C #; No sé lo suficiente sobre lo que =hace exactamente en VB para decirlo con certeza.
Jon Skeet
@JonSkeet Bastante justo.
Servy
Por msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , en la sección "Programación sin tipo con operadores de comparación relacional": =, junto con todos los otros operadores de comparación relacionales, tales como <, >=, etc. , reciben un tratamiento especial cuando ambos o cualquier lado del operador lo está Object. Este tratamiento especial se realiza para que los programadores de VB6, que están acostumbrados a usar un tipo conocido como Varianten VB pre.NET, puedan usarlo Objecten VB.Net de la forma en que lo han hecho Variantantes.
rskar
Para decirlo de otra manera, y dejando de lado los efectos de la sobrecarga y Option Strict On, VB =está predispuesto a desempaquetar un Objecthasta que pueda llegar a un String o numérico.
rskar