c# - reales - propiedades de los numeros enteros
¿Por qué el cero entero no es igual a cero largo? (6)
Un código extraño que acabo de descubrir en C # (también debería ser válido para otros lenguajes CLI utilizando las structs
de .NET).
using System;
public class Program
{
public static void Main(string[] args)
{
int a;
long b;
a = 0;
b = 0;
Console.WriteLine(a.Equals(b)); // False
Console.WriteLine(a.Equals(0L)); // False
Console.WriteLine(a.Equals((long)0)); // False
Console.WriteLine(a.Equals(0)); // True
Console.WriteLine(a.Equals(a)); // True
Console.WriteLine(a == b); // True
Console.WriteLine(a == 0L); // True
Console.WriteLine();
Console.WriteLine(b.Equals(a)); // True
Console.WriteLine(b.Equals(0)); // True
Console.WriteLine(b.Equals((int)0)); // True
Console.WriteLine(b.Equals(b)); // True
Console.WriteLine(b == a); // True
Console.WriteLine(b == 0); // True
}
}
Dos puntos interesantes aquí (suponiendo que a
es int
y b
es long
):
-
a != b
, perob == a
; -
(a.Equals(b)) != (a == b)
¿Hay alguna razón por la cual la comparación fue implementada de esta manera?
Nota: se usó .NET 4 si hay alguna diferencia.
C # no hace casting automático. La función de igual compara tipos y valores. Al igual que === en JS.
En general, no se supone que los métodos Equals()
devuelvan verdadero para objetos de diferentes tipos.
a.Equals(b)
llama a int.Equals(object)
, que solo puede devolver true para Int32
s en caja:
public override bool Equals(Object obj) {
if (!(obj is Int32)) {
return false;
}
return m_value == ((Int32)obj).m_value;
}
b.Equals(a)
llama a long.Equals(long)
después de convertir implícitamente el int
en un long
.
Por lo tanto, compara los dos long
s directamente, volviendo verdadero.
Para entender más claramente, mire la IL generada por este ejemplo más simple (que imprime Verdadero Falso Verdadero):
int a = 0;
long b = 0L;
Console.WriteLine(a == b);
Console.WriteLine(a.Equals(b));
Console.WriteLine(b.Equals(a));
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldc.i4.0
IL_0003: conv.i8
IL_0004: stloc.1
IL_0005: ldloc.0 //Load a
IL_0006: conv.i8 //Cast to long
IL_0007: ldloc.1 //Load b
IL_0008: ceq //Native long equality check
IL_000A: call System.Console.WriteLine //True
IL_000F: ldloca.s 00 //Load the address of a to call a method on it
IL_0011: ldloc.1 //Load b
IL_0012: box System.Int64 //Box b to an Int64 Reference
IL_0017: call System.Int32.Equals
IL_001C: call System.Console.WriteLine //False
IL_0021: ldloca.s 01 //Load the address of b to call a method on it
IL_0023: ldloc.0 //Load a
IL_0024: conv.i8 //Convert a to Int64
IL_0025: call System.Int64.Equals
IL_002A: call System.Console.WriteLine //True
Las sobrecargas de operadores y métodos, así como los operadores de conversión, se evalúan en el momento de la compilación, a diferencia de las modificaciones del método virtual que se evalúan en tiempo de ejecución. La expresión someIntVar.Equals(someNumericQuantity)
tiene someIntVar.Equals(someNumericQuantity)
relación con la expresión someObjectVarThatHoldsAnInt.Equals(someNumericQuantity)
. Si pretendiera que el método virtual Object.Equals
tiene un nombre diferente (como IsEquivalentTo
), y sustituya ese nombre en cada lugar donde se usa el método virtual, esto sería mucho más claro. Un cero entero puede ser numéricamente igual a un cero largo, pero eso no significa que sean semánticamente equivalentes.
Tal separación en el significado entre Equals
e IsEquivalentTo
, incidentalmente, también habría ayudado a evitar la confusión en la definición de este último. Es posible definir una relación de equivalencia significativa para objetos arbitrarios: la ubicación de almacenamiento X
debe considerarse equivalente a la ubicación de almacenamiento Y
si el comportamiento de todos los miembros de la primera siempre será equivalente a los miembros correspondientes de la última, y la única manera de determinar si X
e Y
refieren al mismo objeto sería usar Reflection o ReferenceEquals
. Aunque 1.0m.Equals(1.00m)
es y debería ser cierto, 1.0m.IsEquivalentTo(1.00m)
debe ser falso. Desafortunadamente, el uso del mismo nombre para el método de prueba de equivalencia de objetos y el método de prueba de igualdad numérica Decimal
llevó a Microsoft a definir que el primero se comporte como el último.
No son lo mismo porque incluso los tipos simples se heredan de System.Object: en realidad son objetos y los diferentes tipos de objetos, incluso con los mismos valores de propiedad, no son iguales.
Ejemplo:
Podría tener un objeto Colaborador con una sola propiedad: Nombre (cadena) y un objeto asociado con una sola propiedad: Nombre (cadena)
El compañero de trabajo David no es lo mismo que Parner David. El hecho de que sean tipos de objetos diferentes los distingue.
En su caso, utilizando .Equals (), no está comparando valores, está comparando objetos. El objeto no es "0", es un System.Int32 con un valor de cero y un System.Int64 con un valor de cero.
Ejemplo de código basado en la pregunta en el comentario a continuación:
class CoWorker
{
public string Name { get; set; }
}
class Partner
{
public string Name { get; set; }
}
private void button1_Click(object sender, RoutedEventArgs e)
{
CoWorker cw = new CoWorker();
cw.Name = "David Stratton";
Partner p = new Partner();
p.Name = "David Stratton";
label1.Content = cw.Equals(p).ToString(); // sets the Content to "false"
}
También está el problema de reducir o ampliar la conversión. Un cero long
siempre es igual a un cero int
, pero no al revés.
Cuando se compara un largo con un int, solo se comparan los 32 bits menos significativos y se ignora el resto, por lo que la int.Equals(long)
no puede garantizar la igualdad incluso si los bits más bajos coinciden.
int a = 0;
long b = 0;
Trace.Assert(a.Equals((int)b)); // True 32bits compared to 32bits
Trace.Assert(a.Equals((long)b)); // False 32bits compared to 64bits (widening)
Trace.Assert(b.Equals((long)a)); // True 64bits compared to 64bits
Trace.Assert(b.Equals((int)a)); // True 64bits compared to 32bits (narrowing)
También considere el caso donde los 32 bits inferiores son iguales, pero los superiores no lo son.
uint a = 0;
ulong b = 0xFFFFFF000000;
Trace.Assert((uint)a == (uint)b); // true because of a narrowing conversion
Trace.Assert((ulong)a == (ulong)b); // false because of a widening conversion
porque Equals compara objetos y los objetos a y b son diferentes. Tienen el mismo valor pero son diferentes como objetos.
Este enlace puede ayudarlo: http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx