Kiedy w środowisku .NET tworzymy obiekt składa się on z dwóch części, pierwsza z nich to zawartość a druga to referencja do tej zawartości.
Dla przykładu, jeżeli utworzymy obiekt taki jak w poniższym przykładzie:

  • ”Przykładowa treść” jest zawartością;
  • ”o” jest referencją do tej zawartości.
object o = "Przykładowa treść";
I teraz kluczowa odpowiedź na pytanie: == porównuje czy referencja do obiektu jest taka sama podczas gdy Equals() porównuje czy zawartość jest taka sama.
object o = "Przykładowa treść";
object o1 = o;
// znak == - sprawdzenie czy referencja jest taka sama || TRUE
Console.WriteLine(o == o1);
// Equals - sprawdzenie czy zawartość jest taka sama || TRUE
Console.WriteLine(o.Equals(o1)); 
Console.ReadKey();
Sprawdźmy teraz poniży kod, w którym mamy taką samą zawartość ale inne referencje. Jeśli uruchomisz taki program, porównanie przy pomocy == zwróci false a wywołanie Equals() zwróci true.
object o = "Przykładowa treść";
object o1 = new string("Przykładowa treść".ToCharArray());
// znak == - sprawdzenie czy referencja jest taka sama || FALSE
Console.WriteLine(o == o1);
// Equals - sprawdzenie czy zawartość jest taka sama || TRUE
Console.WriteLine(o.Equals(o1));
Console.ReadKey();
Warto również pamiętać, że używając porównań dla typu łańcuchowego (string) zawsze dochodzi do porównania zawartości. Innymi słowy, nie ważne czy użyjesz operatora == czy metody Equals() zawsze dojdzie do porównania zawartości. Aby potwierdzić teorię poniżej przykład:
string str = "Przykładowa treść";
string str2 = str;
// znak == - sprawdzenie czy referencja jest taka sama || TRUE
Console.WriteLine(str == str2);
// Equals - sprawdzenie czy zawartość jest taka sama || TRUE
Console.WriteLine(str.Equals(str2));
string str3 = "Przykładowa treść";
string str4 = "Przykładowa treść";
// znak == - w przypadku typu string nie sprawdzamy referencji a zawartość || TRUE
Console.WriteLine(str3 == str4);
// Equals - sprawdzenie czy zawartość jest taka sama || TRUE
Console.WriteLine(str3.Equals(str4));
Console.ReadKey();