Tipos · comparación

== frente a .Equals() en C#

Coinciden en la mayoría del código de principiante — y las diferencias son justo donde se esconden los bugs: el manejo de null y qué significa "igual" para cada tipo.

Para primitivos: comportamiento idéntico

int x = 5;
int y = 5;
bool f = false;

Console.WriteLine(x == y);      // comparación de valores
Console.WriteLine(x.Equals(y)); // la misma respuesta para int
Console.WriteLine(f.Equals(false));

Para int, double, bool, char no hay diferencia real — usa ==, se lee mejor.

Para strings: mismo resultado, mecanismo distinto

string a = "manzana";

Console.WriteLine(a == "manzana");     // True  — C# hace que == compare CONTENIDO
Console.WriteLine(a == "Manzana");     // False — las mayúsculas importan, siempre
Console.WriteLine(a.Equals("manzana"));   // True  — contenido otra vez
Console.WriteLine(a.Equals("Manzana"));   // False — misma regla de mayúsculas

Ambos comparan contenido y ambos distinguen mayúsculas. (En otros lenguajes == compara referencias para strings — la clásica confusión C#/Java; en C# el operador se sobrecargó para hacer lo útil.)

Diferencia 1: manejo de null

La verdadera divisoria. == convive con null; llamar a un método sobre null explota:

string s = null;

// == es una comprobación estática — null en cualquier lado está bien
Console.WriteLine(s == null);

// .Equals() es una LLAMADA a un método sobre s — s es null, así que protégelo antes:
if (s != null)
{
    Console.WriteLine(s.Equals("x"));
}
else
{
    Console.WriteLine("seguro");   // aquí entra — llamar a s.Equals habría lanzado
}

Regla práctica: cuando cualquiera de los lados podría ser null, usa == (o string.IsNullOrEmpty), no .Equals().

Diferencia 2: qué significa "igual" es redefinible

Equals() es un método que una clase puede redefinir; == es un operador que una clase puede o no sobrecargar. Para tus propias clases, ninguno compara contenido por defecto — ambos son identidad (la demo a == b → False del artículo struct vs class):

class Perro { public string Nombre; }

Perro d1 = new Perro { Nombre = "Rex" };
Perro d2 = new Perro { Nombre = "Rex" };
Console.WriteLine(d1 == d2);        // False — objetos distintos
Console.WriteLine(d1.Equals(d2));   // False — igual, salvo que se redefina Equals

Los records (C# 9+) y los struct cambian ese valor por defecto a comparación de valores — una de las razones por las que los records se adueñaron de los tipos de datos.

El ganador del día a día: comparar sin mayúsculas

La entrada del usuario no respeta tus mayúsculas. La herramienta idiomática no es == ni Equals a secas — es Equals con un tipo de comparación (en Visual Studio; el motor del navegador aún no tiene esa sobrecarga):

string input = "SI";

// estos tres son los patrones del mundo real:
input == "si"                                        // False — distingue mayúsculas
input.Equals("si", StringComparison.OrdinalIgnoreCase)   // True
input.ToLower() == "si"                              // True — la versión rápida y sucia

Prefiere StringComparison.OrdinalIgnoreCase a las cadenas de ToLower() — no asigna memoria y pone la intención a la vista.

Tabla de decisión

ComparandoUsa
números, bool, char==
strings, importan mayúsculas==
strings, sin mayúsculasEquals(…, OrdinalIgnoreCase)
algo que podría ser null== (o IsNullOrEmpty)
tus propios tipos de igualdad por contenidohazlo record, o redefine Equals