== a Equals

0

Mam takie dwa szybkie pytania.

  1. Jesli porownujemy int z intem, string ze stringiem to nie ma roznicy czy wybierzemy == a Equals, roznica pojawia sie dopiero kiedy chodzi o object?
  2. Ogolnie nie potrafilem ogarnac czym to sie rozni, dopoki nie wpadlem na taki przyklad:
    object a = 1
    object b = 0+1;
    W == wyjdzie false
    W Equals wyjdzie true.
    Czyli == porownuje zawartosc obiektow, natomiast Equals wynik koncowy obiektow? Dobrze kombinuje?
1

W przypadku object (i domyślnie typów referencyjnych) == porównuje referencje. Natomiast Equals jest override, więc zostanie wywołana metoda na faktycznym obiekcie, co za tym idzie w tej sytuacji zadziała implementacja z typu Int32, czyli zostaną porównane wartości.

0

Polecam "Albahari J. - C# 6.0 w pigułce", fajnie wyjaśnia podobne niuanse. Powyższy przypadek opisuje podrozdział "Sprawdzanie równości".

W poniższym przykładzie kompilator powiąże operator ==
z typem int, ponieważ zmienne x i y są tego typu:
int x = 5;
int y = 5;
Console.WriteLine (x == y); // prawda
Natomiast w tym przypadku kompilator powiąże operator == z typem object:
object x = 5;
object y = 5;
Console.WriteLine (x == y); // fałsz
Ponieważ object jest klasą (a więc typem referencyjnym), operator == tej klasy działa wg zasad równości
referencyjnej i zgodnie z nimi porównuje x i y. W wyniku otrzymujemy fałsz, ponieważ zmienne
te odnoszą się do różnych opakowanych obiektów zapisanych na stercie.
Wirtualna metoda Object.Equals
Wartości x i y z poprzedniego przykładu można poprawnie porównać za pomocą wirtualnej metody
Equals z przestrzeni nazw System.Object, która jest dostępna dla wszystkich typów:
object x = 5;
object y = 5;
Console.WriteLine (x.Equals (y)); // prawda

1

Co tak naprawdę dzieje się w takiej sytuacji :
6 przypadków.

using System;

namespace ConsoleApp43
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10, b = 10;
            Dog pies1 = new Dog(), pies2 = new Dog();

            Console.WriteLine(object.Equals(pies1, pies2));
            Console.WriteLine(object.Equals(a, b));
            Console.WriteLine(a == b);
            Console.WriteLine(int.Equals(a, b));
            Console.WriteLine(a.Equals(b));

            object c = b;

            Console.WriteLine(a.Equals(c));
        }
    }

    class Dog
    {

    }
}
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       126 (0x7e)
  .maxstack  2
  .locals init ([0] int32 a,
           [1] int32 b,
           [2] class ConsoleApp43.Dog pies1,
           [3] class ConsoleApp43.Dog pies2,
           [4] object c)
  IL_0000:  nop

// pierwszy przypadek
  IL_0001:  ldc.i4.s   10
  IL_0003:  stloc.0
  IL_0004:  ldc.i4.s   10
  IL_0006:  stloc.1
  IL_0007:  newobj     instance void ConsoleApp43.Dog::.ctor()
  IL_000c:  stloc.2
  IL_000d:  newobj     instance void ConsoleApp43.Dog::.ctor()
  IL_0012:  stloc.3
  IL_0013:  ldloc.2
  IL_0014:  ldloc.3
  IL_0015:  call       bool [mscorlib]System.Object::Equals(object,
                                                            object)
  IL_001a:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_001f:  nop

// drugi przypadek
  IL_0020:  ldloc.0
  IL_0021:  box        [mscorlib]System.Int32
  IL_0026:  ldloc.1
  IL_0027:  box        [mscorlib]System.Int32
  IL_002c:  call       bool [mscorlib]System.Object::Equals(object,
                                                            object)
  IL_0031:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0036:  nop

// trzeci przypadek
  IL_0037:  ldloc.0
  IL_0038:  ldloc.1
  IL_0039:  ceq
  IL_003b:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0040:  nop

// czwarty przypadek
  IL_0041:  ldloc.0
  IL_0042:  box        [mscorlib]System.Int32
  IL_0047:  ldloc.1
  IL_0048:  box        [mscorlib]System.Int32
  IL_004d:  call       bool [mscorlib]System.Object::Equals(object,
                                                            object)
  IL_0052:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0057:  nop

// piąty przypadek
  IL_0058:  ldloca.s   a
  IL_005a:  ldloc.1
  IL_005b:  call       instance bool [mscorlib]System.Int32::Equals(int32)
  IL_0060:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0065:  nop

// szósty przypadek
  IL_0066:  ldloc.1
  IL_0067:  box        [mscorlib]System.Int32
  IL_006c:  stloc.s    c
  IL_006e:  ldloca.s   a
  IL_0070:  ldloc.s    c
  IL_0072:  call       instance bool [mscorlib]System.Int32::Equals(object)
  IL_0077:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_007c:  nop
  IL_007d:  ret
} // end of method Program::Main

Warto omówić przypadek 2.
Widać że w przypadku typu wartościowego następuje opakowanie ,następnie wywoływana jest metoda
Object::Equals(object, object)

.method public hidebysig static bool  Equals(object objA,
                                             object objB) cil managed
{
  .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  bne.un.s   IL_0006
  IL_0004:  ldc.i4.1
  IL_0005:  ret
  IL_0006:  ldarg.0
  IL_0007:  brfalse.s  IL_000c
  IL_0009:  ldarg.1
  IL_000a:  brtrue.s   IL_000e
  IL_000c:  ldc.i4.0
  IL_000d:  ret
  IL_000e:  ldarg.0
  IL_000f:  ldarg.1
  IL_0010:  callvirt   instance bool System.Object::Equals(object)
  IL_0015:  ret
} // end of method Object::Equals

Jeśli referencje są równe to zwracana jest wartość true ( tylko w przypadku typów referencyjnych może tak być )
jeśli , któraś z referencji jest null to zwracane jest - false
Jeśli nie zachodzi żaden z tych przypadków tzn. referencje nie są równe i nie są null
wywoływana jest kolejna metoda tym razem instancyjna :
instance bool System.Object::Equals(object)
która wygląda tak:

.method public hidebysig newslot virtual 
        instance bool  Equals(object obj) cil managed
{
  .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       8 (0x8)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  call       bool System.Runtime.CompilerServices.RuntimeHelpers::Equals(object,
                                                                                   object)
  IL_0007:  ret
} // end of method Object::Equals

ta metoda z kolei wywołuje następną metodę, która wygląda tak:

.method public hidebysig static bool  Equals(object o1,
                                             object o2) cil managed internalcall
{
  .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) 
} // end of method RuntimeHelpers::Equals

// Denotes the method body is provided bythe CLI itself

Z tego kodu wynika, że metoda System.Object::Equals(object, object) nie jest wirtualna i nie może być nadpisana w strukturze Int32
, natomiast metoda bool Equals(object obj) jest nadpisana i wygląda tak:

.method public hidebysig newslot virtual final 
        instance bool  Equals(int32 obj) cil managed
{
  .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = ( 01 00 00 00 ) 
  .custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldind.i4
  IL_0002:  ldarg.1
  IL_0003:  ceq
  IL_0005:  ret
} // end of method Int32::Equals

Metoda ta ma modyfikator : override sealed

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.