Por que o C # falha ao comparar dois tipos de objetos, mas o VB não?


152

Eu tenho dois objetos em c # e não sei se é booleano ou qualquer outro tipo. No entanto, quando eu tento comparar esses c # falha em dar a resposta certa. Eu tentei o mesmo código com o VB.NET e foi isso!

Alguém pode me dizer como corrigir isso, se houver uma solução?

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
e se você alterar o comparador de igualdade a.Equals(b)?
21713 Jason Meckley

8
Esta é uma boa pergunta para fins pedagógicos.
Lobo

10
Porque o seu código VB.NET não é igual ao seu código C #.
Security Hound

9
Quando você atribui a avocê, obtenha o boxe e crie uma caixa contendo true. Quando você atribui a bvocê, obtém outra caixa contendo também true. Ao comparar ae b, como os dois são do tipo tempo de compilação object, você chama a sobrecarga operator ==(object, object)definida pela Especificação de idioma do C #. Essa sobrecarga verifica se as referências vão para o mesmo objeto. Como você tem duas caixas, o resultado é falsee a instrução "under" ifnão será executada. Para entender isso melhor, tente alterar a atribuição bpara: object b = a;Agora você tem apenas uma caixa.
Jeppe Stig Nielsen

3
Tive ocasião, antes de dizer "Tenha cuidado assumindo que VB.NET e C # são o mesmo idioma falado com um sotaque diferente - eles não são"
AakashM

Respostas:


168

Em C #, o ==operador (quando aplicado a expressões de tipo de referência) executa uma verificação de igualdade de referência, a menos que esteja sobrecarregada . Você está comparando duas referências que resultam de conversões de boxe, portanto, essas são referências distintas.

EDIT: Com tipos que sobrecarregam ==, você pode obter um comportamento diferente - mas isso é baseado no tipo de expressões em tempo de compilação . Por exemplo, stringfornece ==(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

Aqui, a primeira comparação está usando o operador sobrecarregado, mas a segunda está usando a comparação de referência "padrão".

No VB, o =operador faz muito mais trabalho - nem sequer é equivalente ao uso object.Equals(x, y), pois coisas como isso Option Comparepodem afetar a comparação do texto.

Fundamentalmente, os operadores não funcionam da mesma maneira e não pretendem funcionar da mesma maneira.


17
+1 Eu sabia que você vai estar ao redor, você AMA esses tipos de perguntas misteriosas :)
Abdusalam Ben Haj

3
@AbZy: Eu esperava poder fornecer uma explicação mais detalhada do que =fez no VB, mas a especificação não é muito clara.
21813 Jon Skeet

coisa interessante, mas mudando objeto a dinâmica se comporta igual a VB
VladL

4
@ Vladl: Sim, porque então ele irá pelos tipos de tempo de execução e executará a bool == boolcomparação.
111313 Jon Skeet

1
@Mahdi Lobo pode ter fornecido código, mas sua resposta também está errada, diferente da de Jon.
Servy

79

Além da resposta de Jon, que explica o lado C #, aqui está o que o VB faz:

No VB com Option Strict On, uma comparação via = sempre testa a igualdade de valor e nunca a igualdade de referência. De fato, seu código nem compila uma vez que você alterna Option Strict Onporque System.Objectnão define um Operator=. Você deve sempre ter essa opção ativada, pois captura bugs com mais eficiência do que uma armadilha de venus (embora no seu caso particular esse comportamento negligente realmente faça a coisa certa). 1

Na verdade, com Option Strict On, VB comporta-se ainda mais rigoroso do que o C #: Em C #, a == b quer gatilhos uma chamada para SomeType.operator==(a, b)ou, se este não existir, comparação de igualdade de referência invoca (que é equivalente a chamar object.ReferenceEquals(a, b)).

No VB, por outro lado, a comparação a = b sempre chama o operador de igualdade. 2 Se você quiser usar a comparação de igualdade de referência, precisará usar a Is b(que é, mais uma vez, o mesmo que Object.ReferenceEquals(a, b)).


1) Aqui está uma boa indicação do porquê usar Option Strict Offuma péssima idéia: usei o VB.NET por quase uma década, desde o lançamento oficial do .NET até alguns anos atrás, e não faço ideia do que a = bfaz Option Strict Off. Faz algum tipo de comparação de igualdade, mas o que exatamente acontece e por que, não faz ideia. dynamicPorém, é mais complexo que o recurso do C # (porque isso depende de uma API bem documentada). Aqui está o que o MSDN diz:

Como Option Strict Onfornece digitação forte , evita conversões de tipo não intencionais com perda de dados, desabilita a ligação tardia e melhora o desempenho, seu uso é altamente recomendado.

2) Jon mencionou uma exceção, strings, em que a comparação de igualdade faz mais algumas coisas por razões de compatibilidade com versões anteriores.


4
+1. Eu acho que esse é um caso em que os designers do VB.NET conseguiram fazer a linguagem "apenas funcionar" para programadores vindos do VB6 e VBA, onde o OOP é muito menos proeminente e, portanto, o conceito de igualdade de referência é muito menos importante. Um codificador VB pode escrever um bom código de trabalho sem pensar muito em objetos e assim por diante.
John M Gant

5
+1 Isso não é tão votado quanto deveria. Não utilizar Option Strict Ontem de ser considerada uma ofensa criminal ...
Deer Hunter

1
@JohnMGant: Um codificador que não entende o significado da identidade de referência pode ser capaz de escrever código que funcione, mas é improvável que realmente saiba o que as coisas podem ser alteradas com segurança, que mudanças sempre quebram as coisas e que mudanças podem parecem funcionar, mas causam efeitos colaterais desagradáveis ​​indesejados (por exemplo, fazer com que o que deveriam ser referências a diferentes objetos mutáveis ​​que tenham o mesmo estado sejam referências ao mesmo objeto). Se os objetos raramente são alterados, essa alteração pode não causar problemas imediatos, mas pode fazer surgir erros difíceis de encontrar mais tarde.
supercat 14/08

4

Instâncias de objeto não são comparadas com o operador "==". Você deve usar o método "igual". Com o operador "==" estão comparando referências, não objetos.

Tente o seguinte:

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

Agora, tente o seguinte:

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

1
Isso é simplesmente porque você não substituiu operator ==. Se você substituir esse operador e não for igual, sua saída será revertida. Não há nada inerente à comparação de referências operator ==e nada inerente à comparação de valores em Equals. São apenas duas maneiras de determinar a igualdade; ambos têm implementações padrão de uma comparação de referência e podem ser substituídos para fazer o que você quiser. A única outra diferença é que Equalsé virtual e operator ==não é.
Servy

1
@ Service: Observe que você não pode substituir == - você só pode sobrecarregá- lo.
21813 Jon Skeet

1
Desculpe -1. Esta resposta é simplesmente incorreta e não deve ser a aceita.
21413 Konrad Rudolph

Em algum lugar, há uma pergunta sobre Java aguardando esta resposta.
Chad Schouggins

3

O problema é que o operador == em C # é uma chamada para um método estático (bem, talvez não tecnicamente, mas pode ser como tal) com base no tipo de tempo de compilação dos dois parâmetros. Quais são os tipos reais de tempo de execução desses objetos não importa.

Com base nesse tipo de tempo de compilação, o compilador determinará qual implementação operator ==usar. Pode usar o padrãoobject implementação , pode usar uma das sobrecargas numéricas fornecidas pelo idioma ou pode ser uma implementação definida pelo usuário.

Isso é diferente do VB, pois o VB não determina a implementação no momento da compilação. Ele espera até o tempo de execução e inspeciona os dois parâmetros que são dados para determinar qual implementação do ==operador ele deve usar.

Seu código contém valores booleanos, mas eles estão em variáveis ​​que são do tipo object. Como a variável é do tipo object, o compilador C # usa a objectimplementação de ==, que compara as referências , não as instâncias do objeto. Como os valores booleanos são caixas, eles não têm a mesma referência, mesmo que seus valores sejam os mesmos.

O código VB não se importa com o tipo da variável. Ele espera até o tempo de execução e, em seguida, verifica as duas variáveis, vê que elas são realmente do tipo booleano e, portanto, usa a ==implementação do operador booleano . Essa implementação compara os valores dos booleanos, não suas referências (e os booleanos serão unboxed antes de chamar a chamada desse operador, portanto, uma comparação de referência nem faz mais sentido). Como os valores dos booleanos são os mesmos, ele retorna true.


Isso parece bom para o C #; Eu não sei o suficiente sobre exatamente o que =faz no VB para dizer com certeza.
21813 Jon Skeet

@JonSkeet Fair o suficiente.
Servy

Por msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , na seção "Programação typeless com Comparação Operadores relacionais": =, juntamente com todos os outros operadores de comparação relacionais como <, >=, etc. , recebem tratamento especial quando ambos ou ambos os lados do operador estiverem Object. Esse tratamento especial é feito para que os programadores do VB6, que estão acostumados a usar um tipo conhecido Variantno VB pré-.NET, possam fazer uso do ObjectVB.Net da maneira que eles usavam Variantantes.
rskar

Em outras palavras, e deixando de lado os efeitos da sobrecarga e Option Strict On, o VB =é direcionado para o unboxing an Objectaté que ele possa chegar a uma String ou numérica.
rskar
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.