Error del compilador · error CS0165
Uso de la variable local no asignada 'x'
Lees una variable antes de que algo se haya guardado en ella. A diferencia de la mayoría de lenguajes, C# se niega a darte un cero silencioso — te detiene en compilación, porque "leer basura por accidente" es justo el bug que nunca encuentras en ejecución.
Míralo ocurrir
int total;
Console.WriteLine(total + 1); // 'total' se declaró pero nunca recibió valor
int total; reserva el nombre y el espacio — no pone un valor ahí. Leerla carece de sentido, y C# te lo dice. La solución trivial:
int total = 0;
Console.WriteLine(total + 1);
El caso interesante: asignar no es "definitivamente asignar"
La lección real de CS0165 es que C# exige que la variable esté asignada en todos los caminos que pueden llegar a tu lectura. Asignar dentro de un if no cuenta — el compilador planifica para la rama que no entra:
string etiqueta;
if (puntuacion > 50)
{
etiqueta = "aprobado"; // solo se asigna si la condición se cumple
}
Console.WriteLine(etiqueta); // error CS0165: ¿y si puntuacion era 30?
(El análisis completo de ramas excede la comprobación simple del motor: este valida "¿se asignó alguna vez antes de la lectura?"; Visual Studio aplica la regla completa de "todos los caminos".)
Solución 1: un valor por defecto razonable antes de la rama
void Informe(int puntuacion)
{
string etiqueta = "suspenso"; // el valor por defecto cubre todas las rutas
if (puntuacion > 50)
{
etiqueta = "aprobado";
}
Console.WriteLine(etiqueta);
}
Informe(90);
Informe(30);
Solución 2: que el compilador pueda probarlo — añade else
int x = -5;
string signo;
if (x > 0)
{
signo = "positivo";
}
else
{
signo = "negativo o cero";
}
Console.WriteLine(signo); // OK: if+else cubre todas las rutas
Solución 3: inicializar en la declaración (el valor por defecto idiomático)
int sum = 0; // los acumuladores empiezan en 0 — siempre
for (int i = 1; i <= 5; i++)
{
sum += i;
}
Console.WriteLine("Suma tras el bucle: " + sum);
¿Por qué no poner 0 por defecto en todo?
Los campos de una clase sí tienen valor por defecto (0, null, false) — porque una clase siempre ejecuta su constructor. Las locales no, porque los diseñadores de C# decidieron: si nunca escribiste un valor, leerlo es casi seguro un error. El error es el lenguaje ayudándote — la misma filosofía que CS0029: niega a adivinar, obliga a decir qué quieres.
Preguntas frecuentes
¿Cómo corrijo CS0165?
Se lee una local antes de asignarla. Acumuladores y contadores: inicializa al declarar. Valores calculados en ramas: valor por defecto o else que cubra todos los caminos.
¿Por qué no hay cero por defecto?
Los campos sí lo tienen; las locales no es deliberado — leer sin asignar casi siempre es un bug, y un error de compilación es mejor que un cero silencioso.
Lista de comprobación
- ¿Acumulador o contador? Inicialízalo al declarar (
int sum = 0;). - ¿Valor calculado en ramas? Dale un valor por defecto o añade
elsepara que todas las rutas asignen. - ¿La declaras para rellenarla después? Vale — pero no la leas antes; mueve la lectura debajo de la asignación.
- Los parámetros
outcuentan como "asignados por el método" —int.TryParse(s, out int n)es legal aunque nunca asignesn.