Una variable es una ubicación de almacenamiento con nombre y un tipo fijo. En C# la declaras con un tipo y un nombre, y opcionalmente le das un valor en la misma sentencia. El tipo no cambia después: una variable int contiene números enteros mientras exista.
Salida:
Priya bought 5 items for 20.25
10
Asignar un valor del tipo equivocado es un error de compilación: quantity = "five"; falla con CS0029 (cannot implicitly convert type 'string' to 'int'). Las conversiones entre tipos son explícitas; consulta conversión de tipos.
var: dejar que el compilador deduzca el tipo
var declara una variable local cuyo tipo se deduce de su inicializador. No es tipado dinámico: el tipo queda fijado en tiempo de compilación, exactamente igual que si lo hubieras escrito.
Salida:
String
Int32
Decimal
Double
List`1
var tiene tres reglas. Solo funciona con variables locales (no con campos ni parámetros). La variable debe inicializarse en la declaración: var x; es el error CS0818, "Implicitly-typed variables must be initialized". Y el inicializador debe tener un tipo: var nothing = null; no compila, porque null por sí solo no tiene tipo.
Cuándo usarlo es cuestión de estilo del equipo. La pauta habitual es usar var cuando el tipo es evidente por el lado derecho, y escribir el tipo cuando no lo es:
var orders = new List<Order>(); // obvious: var is fine
var reader = new StreamReader(path); // obvious
decimal discount = CalculateDiscount(); // not obvious: name the type
int count = items.Count(); // makes the intent clear
Una trampa sutil: var total = 0; hace que total sea un int, así que sumarle 2.5 después falla, y var price = 10; es un int, no un decimal. Cuando el tipo del literal no es el que quieres, escribe el tipo o añade un sufijo (10m, 0.0).
Ámbito
Una variable local existe desde su declaración hasta el final del bloque { } en el que se declaró. Las variables declaradas en la cabecera de un bucle o en el cuerpo de un if no son visibles después.
Salida:
high
240
A diferencia de C y C++, C# no permite que un bloque interior declare una variable local con el mismo nombre que otra de un bloque que lo contiene (error CS0136): el nombre debe significar una sola cosa en todo el método. Dos bloques hermanos sí pueden reutilizar un nombre, como hacen dos bucles for separados que declaran cada uno i.
Asignación definitiva
Las variables locales no tienen valor por defecto. El compilador sigue cada camino del método y se niega a compilar si una variable podría leerse antes de haberse asignado:
int discount;
if (isMember)
{
discount = 10;
}
Console.WriteLine(discount);
// error CS0165: Use of unassigned local variable 'discount'
Arréglalo inicializando en la declaración (int discount = 0;) o asignando en todas las ramas (añade un else). Los campos y los elementos de los arrays son distintos: se ponen automáticamente al valor por defecto de su tipo (0, false, null).
const: constantes de tiempo de compilación
Un const es un valor fijado en tiempo de compilación. Debe inicializarse con una expresión constante (un literal, otra constante o aritmética sobre ellos), y solo puede ser un número, char, bool, string, un enum o null.
Salida:
Price in EUR
120.00 EUR
350
Un campo const es implícitamente static, así que se accede a él mediante el nombre del tipo (Pricing.VatRate), y escribir static const es un error. El compilador copia el valor en cada lugar que lo usa, por eso las constantes no cuestan nada en tiempo de ejecución, y también por eso un const público de una biblioteca debería ser un valor que nunca vaya a cambiar (como el número de días de una semana).
readonly y static readonly
Un campo readonly solo puede asignarse en su declaración o en un constructor de su clase. Después de la construcción no puede cambiar. A diferencia de const, su valor se calcula en tiempo de ejecución, puede ser de cualquier tipo y puede variar entre objetos.
Salida:
ACC-17 opened 2024-05-02
Launch: 2020-01-15
basic, pro
Las últimas líneas muestran el malentendido habitual: readonly protege el campo, no el objeto al que apunta. Plans siempre hará referencia al mismo array, pero los elementos del array pueden cambiar. Para colecciones de verdad inmutables, usa ReadOnlyCollection<T> o ImmutableArray<T>.
Resumen de los tres:
const | readonly | static readonly | |
|---|---|---|---|
| Valor conocido | En tiempo de compilación | En tiempo de ejecución | En tiempo de ejecución |
| Tipos permitidos | Números, char, bool, string, enums, null | Cualquiera | Cualquiera |
| Se asigna en | Solo la declaración | La declaración o el constructor | La declaración o el constructor static |
| Por objeto o compartido | Compartido (implícitamente static) | Por objeto | Compartido |
| Variables locales | Sí (const int x = 1;) | No | No |
Convenciones de nombres
Los nombres de C# distinguen mayúsculas y minúsculas (total y Total son variables distintas) y deben empezar por una letra o un guion bajo. Las convenciones de Microsoft, que sigue casi todo el código C#:
- Variables locales y parámetros: camelCase (
orderCount,unitPrice). - Constantes, propiedades, métodos, clases y campos públicos: PascalCase (
MaxItems,VatRate). - Campos privados: _camelCase con un guion bajo delante (
_balance), una convención muy extendida en el código .NET. - Los booleanos se leen como preguntas:
isActive,hasDiscount,canRetry. - Evita las abreviaturas y los prefijos húngaros (
strName,iCount).
Una palabra clave de C# no puede ser un nombre, salvo que le pongas delante @: var @class = "5B"; es válido, lo que a veces resulta útil en código generado o en nombres de campos JSON.
Intercambio y asignación múltiple
Desde C# 7, las tuplas permiten asignar varias variables en una sentencia, lo que da un intercambio en una línea:
Salida:
20 10
Lisbon: 545,000
La segunda forma es la deconstrucción: declara city como string y population como int a partir de una tupla. Más en tuplas.
Preguntas frecuentes
¿Qué es var en C#?
var le dice al compilador que deduzca el tipo de una variable local a partir del valor con el que se inicializa: var count = 5; declara un int. La variable sigue teniendo tipado estático y no puede contener un string más adelante. var solo funciona con variables locales que se inicializan en la misma sentencia.
¿Qué diferencia hay entre const y readonly en C#?
Un const es una constante de tiempo de compilación: su valor debe conocerse al compilar (números, strings, bool, null), es implícitamente static y el valor se copia en cada lugar que lo usa. Un campo readonly se asigna una sola vez en tiempo de ejecución, en su declaración o en un constructor, puede ser de cualquier tipo y puede variar de un objeto a otro.
¿Cuándo debo usar static readonly en lugar de const?
Usa static readonly para un valor compartido que no puede ser una constante de tiempo de compilación, como un DateTime, un array o un objeto, o para un valor público que podría cambiar entre versiones de una biblioteca. Como los valores const se copian en los ensamblados que los usan, cambiar un const público obliga a recompilar todos esos ensamblados.
¿Qué significa "use of unassigned local variable" en C#?
Error CS0165: el compilador encontró un camino en el que se lee una variable local antes de asignarle ningún valor. Las variables locales no tienen valor por defecto en C#. Inicializa la variable al declararla, o asegúrate de que cada rama del if/switch la asigna antes de usarla.
¿Debo usar var o tipos explícitos en C#?
Las dos opciones valen; cada equipo elige un estilo. Una regla habitual es usar var cuando el tipo es evidente por el lado derecho (var list = new List<string>();) y escribir el tipo cuando no lo es (decimal total = GetTotal();). El código del runtime de .NET sigue más o menos esa regla.