Toda aplicación necesita almacenar y manipular datos: el título de un libro, la cantidad de páginas, si un ejemplar está disponible o no. En C#, esos datos se guardan en variables, y cada variable tiene un tipo que determina qué clase de valores puede contener y qué operaciones se pueden hacer con ellos. En esta lección aprenderás a declarar variables, a distinguir entre los tipos de valor y los tipos de referencia, a convertir datos de un tipo a otro, a definir constantes, y a entender el ámbito (scope) de una variable. Usaremos siempre datos del catálogo de BiblioTech —título, ISBN, número de páginas, disponibilidad— como hilo conductor de todos los ejemplos.
Contenido
- Declaración de variables: tipos explícitos y
var - Tipos de valor
- Tipos de referencia
- Conversión de tipos
- Constantes:
constyreadonly - Ámbito de las variables
- Valores por defecto y nulabilidad básica
Declaración de variables: tipos explícitos y var
Declarar una variable en C# significa indicarle al compilador tres cosas: su tipo, su nombre, y (opcionalmente, pero recomendable) un valor inicial. La sintaxis general es:
Por ejemplo, para representar el título de un libro de BiblioTech:
string tituloLibro = "Cien anos de soledad";
int numeroPaginas = 471;
bool estaDisponible = true;
Console.WriteLine(tituloLibro);
Console.WriteLine(numeroPaginas);
Console.WriteLine(estaDisponible);La palabra clave var
C# también permite declarar una variable usando la palabra clave var, dejando que el
compilador infiera (deduzca) el tipo a partir del valor asignado:
var tituloLibro = "Cien anos de soledad"; // el compilador infiere que es 'string'
var numeroPaginas = 471; // el compilador infiere que es 'int'
var estaDisponible = true; // el compilador infiere que es 'bool'Es muy importante entender que var no convierte a C# en un lenguaje de tipado dinámico:
el tipo se sigue fijando en tiempo de compilación, exactamente igual que si lo hubieras
escrito de forma explícita; simplemente el compilador lo deduce por ti a partir del valor
asignado. Por eso, una vez declarada con var, la variable sigue estando "atada" a ese tipo:
var numeroPaginas = 471;
// numeroPaginas = "cuatrocientas"; // ERROR: no se puede asignar un string a una variable inferida como int| Aspecto | Tipo explícito (int x = 5;) |
var (var x = 5;) |
|---|---|---|
| ¿Requiere un valor inicial? | No | Sí, siempre (el compilador necesita el valor para inferir el tipo) |
| ¿El tipo puede cambiar después? | No | No (una vez inferido, es fijo) |
| Cuándo usarlo | Cuando el tipo aporta claridad al leer el código | Cuando el tipo es obvio por el propio valor, o muy largo de escribir |
En este curso usaremos ambas formas según el contexto; ninguna es "mejor" de forma absoluta,
aunque muchos equipos profesionales prefieren var cuando el tipo es evidente por el valor
asignado (como en nuestros ejemplos), y el tipo explícito cuando ayuda a la claridad.
Tipos de valor
Los tipos de valor (value types) son aquellos donde la variable contiene directamente el dato. Cuando copias una variable de tipo valor a otra, se copia el dato completo; cada variable es totalmente independiente de la otra. Los tipos de valor más habituales en C# son los numéricos, el booleano y el carácter:
| Tipo | Representa | Ejemplo | Tamaño aproximado |
|---|---|---|---|
int |
Número entero | 471 |
32 bits |
long |
Número entero grande | 9999999999L |
64 bits |
double |
Número decimal (coma flotante) | 19.99 |
64 bits |
decimal |
Número decimal de alta precisión | 19.99m |
128 bits |
bool |
Valor lógico verdadero/falso | true, false |
1 bit (lógico) |
char |
Un único carácter | 'A' |
16 bits |
Ejemplo aplicado a BiblioTech:
int numeroPaginas = 471;
double precioLibro = 24.90;
decimal precioExacto = 24.90m; // el sufijo 'm' indica que es un 'decimal'
bool estaDisponible = true;
char inicialAutor = 'G';
Console.WriteLine(numeroPaginas);
Console.WriteLine(precioLibro);
Console.WriteLine(precioExacto);
Console.WriteLine(estaDisponible);
Console.WriteLine(inicialAutor);¿double o decimal?
Ambos representan números con decimales, pero tienen un uso distinto:
doublees más rápido y ocupa menos espacio, pero puede tener pequeñas imprecisiones al hacer ciertas operaciones (por su forma interna de representar los números en binario).decimales más preciso, pensado específicamente para cálculos donde la exactitud importa mucho, como el dinero (por ejemplo, el precio de un libro o una multa por retraso en BiblioTech). Su sufijo obligatorio es la letramal final del número literal.
Como norma práctica: para cantidades de dinero, usa siempre decimal; para el resto de
cálculos numéricos con decimales, double suele ser suficiente.
Tipos de referencia
A diferencia de los tipos de valor, los tipos de referencia (reference types) no
almacenan el dato directamente en la variable, sino una referencia (una especie de
"dirección") que apunta a dónde está realmente almacenado el dato. El tipo de referencia más
usado, con diferencia, es string:
La diferencia práctica más importante entre tipos de valor y de referencia aparece al copiar variables:
// Con un tipo de VALOR (int): cada variable es independiente
int paginasLibroA = 471;
int paginasLibroB = paginasLibroA; // se copia el número
paginasLibroB = 500;
Console.WriteLine(paginasLibroA); // sigue mostrando 471: no se ha visto afectadaCon tipos de referencia, veremos en el Módulo 3 (cuando trabajemos con objetos como Libro)
que dos variables pueden apuntar al mismo dato en memoria, de forma que modificar uno a
través de una variable también afecta a lo que se ve a través de la otra. string es un caso
particular de tipo de referencia que se comporta, a efectos prácticos del día a día, de forma
muy parecida a un tipo de valor, gracias a su inmutabilidad (que estudiaremos en detalle
en la próxima lección): una vez creada una cadena de texto, su contenido no puede modificarse;
cualquier operación que "parezca" modificarla en realidad crea una cadena nueva.
| Tipos de valor | Tipos de referencia | |
|---|---|---|
| Ejemplos | int, double, bool, char, decimal |
string, arrays, y (Módulo 3) clases definidas por nosotros |
| Qué contiene la variable | El dato directamente | Una referencia al dato, almacenado en otro lugar de memoria |
| Al copiar una variable | Se copia el dato completo (independiente) | Se copia la referencia (ambas variables pueden apuntar al mismo dato) |
No te preocupes si esta distinción no queda completamente clara todavía: la retomaremos con ejemplos mucho más ricos cuando trabajemos con nuestras propias clases en el Módulo 3, donde sus implicaciones se vuelven más visibles.
Conversión de tipos
A veces necesitamos convertir un dato de un tipo a otro: por ejemplo, un número de páginas
introducido como texto (string) necesita convertirse a int antes de poder hacer cálculos
con él. C# ofrece varias formas de hacerlo.
Conversión implícita
Ocurre automáticamente cuando no hay riesgo de pérdida de información, por ejemplo al pasar de un tipo "más pequeño" a uno "más grande":
int numeroPaginas = 471;
double numeroPaginasComoDecimal = numeroPaginas; // conversión implícita: int -> double, sin pérdida de datosConversión explícita (casting)
Es necesaria cuando sí puede haber pérdida de información (por ejemplo, de double a
int, donde se pierden los decimales). Se indica escribiendo el tipo destino entre
paréntesis:
double precioConDecimales = 24.90;
int precioRedondeado = (int)precioConDecimales; // conversión explícita: se pierde la parte decimal (queda en 24)Convert y Parse/TryParse
La conversión más habitual en la práctica es convertir texto (string) a un tipo numérico, por
ejemplo cuando se lee un dato introducido por el usuario en la consola:
string textoNumeroPaginas = "471";
// Opción 1: usando la clase Convert
int paginas1 = Convert.ToInt32(textoNumeroPaginas);
// Opción 2: usando el método Parse del propio tipo
int paginas2 = int.Parse(textoNumeroPaginas);
Console.WriteLine(paginas1);
Console.WriteLine(paginas2);Ambas opciones lanzan un error en tiempo de ejecución si el texto no se puede convertir
(por ejemplo, si contiene letras). Para evitar que el programa se detenga abruptamente ante un
dato inválido, existe una alternativa más segura: TryParse.
string entradaUsuario = "cuatrocientos"; // un texto no numérico, a modo de ejemplo
bool conversionCorrecta = int.TryParse(entradaUsuario, out int paginas);
if (conversionCorrecta)
{
Console.WriteLine($"Numero de paginas: {paginas}");
}
else
{
Console.WriteLine("El texto introducido no es un numero valido.");
}TryParse devuelve un valor bool indicando si la conversión tuvo éxito, y entrega el
resultado a través de un parámetro de salida (out). No te preocupes si la palabra clave
out resulta nueva: de momento basta con saber que paginas recibirá el número convertido
solo si conversionCorrecta es true. En el Módulo 2, al ver el manejo de excepciones,
entenderás mejor por qué TryParse suele ser preferible a Parse cuando el dato viene de una
fuente externa (como la entrada del usuario) y no podemos garantizar que sea válido.
| Método | Comportamiento ante un dato inválido | Cuándo usarlo |
|---|---|---|
Convert.ToInt32(texto) |
Lanza una excepción | Cuando confías plenamente en el formato del dato |
int.Parse(texto) |
Lanza una excepción | Similar a Convert, específico del tipo |
int.TryParse(texto, out valor) |
Devuelve false, no lanza excepción |
Cuando el dato puede no ser válido (por ejemplo, entrada de usuario) |
Constantes: const y readonly
No todos los datos deben poder cambiar durante la ejecución del programa. Cuando un valor es fijo y conocido de antemano, es buena práctica declararlo como constante, para dejar claro que nunca cambiará y para que el propio compilador impida cualquier intento accidental de modificarlo.
const
Se usa para valores que se conocen ya en tiempo de compilación y nunca van a cambiar:
const int MaximoLibrosPorSocio = 5;
const string NombreSistema = "BiblioTech";
Console.WriteLine($"{NombreSistema} permite un maximo de {MaximoLibrosPorSocio} libros por socio.");
// MaximoLibrosPorSocio = 10; // ERROR: no se puede modificar una constantereadonly
Se usa, sobre todo, en el contexto de clases (Módulo 3) para valores que se fijan una única vez, normalmente al construir el objeto, pero que no se conocen necesariamente en tiempo de compilación (por ejemplo, podrían depender de una fecha o de una entrada externa). Solo lo mencionamos aquí a modo de comparación; su uso real lo veremos con detalle en el Módulo 3 cuando trabajemos con constructores.
const |
readonly |
|
|---|---|---|
| Se conoce el valor en... | Tiempo de compilación | Puede fijarse en tiempo de ejecución (una sola vez) |
| Dónde se usa típicamente | Variables locales o de clase con valor fijo | Miembros de una clase (Módulo 3) |
Ámbito de las variables
El ámbito (scope) de una variable es la región del código donde esa variable existe y
puede utilizarse. En C#, el ámbito de una variable local viene determinado por el bloque
{ } en el que se declara:
{
int numeroPaginas = 471; // 'numeroPaginas' existe desde aquí...
Console.WriteLine(numeroPaginas);
} // ...hasta el cierre de este bloque
// Console.WriteLine(numeroPaginas); // ERROR: 'numeroPaginas' ya no existe fuera del bloqueEste comportamiento se volverá muy relevante en el Módulo 2, cuando trabajemos con bloques de
if y bucles, ya que las variables declaradas dentro de esos bloques no son visibles fuera de
ellos. Por ahora, basta con recordar la regla general: una variable vive dentro de las
llaves donde fue declarada, y desaparece al cerrarse ese bloque.
Valores por defecto y nulabilidad básica
Cada tipo de valor tiene un valor por defecto que recibe automáticamente si se declara sin inicializar explícitamente (aunque, en la práctica, C# obliga a inicializar las variables locales antes de usarlas, por lo que este concepto es más relevante en otros contextos, como los campos de una clase que veremos en el Módulo 3):
| Tipo | Valor por defecto |
|---|---|
int, double, decimal |
0 |
bool |
false |
char |
'\0' (carácter nulo) |
string (y otros tipos de referencia) |
null |
¿Qué es null?
null representa la ausencia de un valor: una variable de tipo referencia (como
string) puede no apuntar a ningún dato todavía. Por ejemplo, imaginemos un libro de
BiblioTech cuyo ISBN todavía no se ha registrado:
string isbnLibro = null; // el libro existe, pero todavía no tiene ISBN asignado
Console.WriteLine(isbnLibro is null); // TrueEl símbolo ? para tipos de valor nulables
Por defecto, los tipos de valor (int, bool, etc.) no pueden ser null: siempre tienen
un valor concreto. Sin embargo, a veces es útil representar "todavía no se sabe" incluso para
un número, por ejemplo, el número de páginas de un libro que aún no se ha catalogado
completamente. Para ello, C# permite convertir un tipo de valor en nulable añadiendo el
símbolo ? después del tipo:
int? numeroPaginas = null; // ahora sí es válido: un 'int?' puede no tener valor
numeroPaginas = 471; // pero también puede tener un valor normal
Console.WriteLine(numeroPaginas);Esto es solo una primera toma de contacto con la idea de nulabilidad; en el Módulo 4
profundizaremos en los tipos de referencia nulables (una característica más moderna que
extiende esta misma idea a string y otros tipos de referencia, ayudando a prevenir errores
relacionados con valores null inesperados). De momento, con saber que ? después de un tipo
de valor permite representar "sin valor todavía" es suficiente.
Errores Comunes y Consejos
- Intentar usar una variable antes de inicializarla: C# no permite leer una variable local que no se ha inicializado; el compilador dará un error. Siempre asigna un valor inicial (aunque sea temporal) antes de usar una variable.
- Perder precisión al usar
doublepara dinero: por su representación interna en binario,doublepuede arrastrar pequeñas imprecisiones en cálculos con decimales. Para precios, multas o cualquier cantidad monetaria, usa siempredecimal. - Usar
Parsecon datos que pueden no ser válidos: si el dato viene de fuera (entrada de usuario, un archivo, una API), usaTryParseen lugar deParseoConvert, para evitar que el programa se detenga con un error si el dato no tiene el formato esperado. - Confundir
constcon una variable normal: una vez declarada una constante, cualquier intento de modificarla es un error de compilación; esto es intencional y forma parte de su utilidad. - Consejo: cuando dudes entre
vary un tipo explícito, pregúntate si el tipo resulta obvio con solo mirar el valor asignado. Si la respuesta es sí,varsuele hacer el código más limpio; si no, el tipo explícito ayuda a la persona que lea el código después.
Ejercicios
-
Declara tres variables para representar un libro de BiblioTech: su título (
string), su número de páginas (int) y si está disponible (bool). Hazlo primero con tipos explícitos y después reescribe las mismas tres declaraciones usandovar. Imprime los tres valores conConsole.WriteLine. -
Un usuario introduce el número de páginas de un libro como texto:
string entrada = "350";. Escribe código que convierta ese texto aintde forma segura usandoTryParse, y que muestre por consola un mensaje distinto según la conversión tenga éxito o no. Después, prueba a cambiar el valor deentradapor un texto no numérico (por ejemplo,"trescientos") y comprueba que el mensaje de error se muestra correctamente. -
Declara una constante
const int DiasPrestamoEstandar = 15;que represente los días habituales de préstamo en BiblioTech. Declara también una variableint? diasExtra = null;que representará una posible ampliación del préstamo, todavía no decidida. Escribe el código necesario para, sidiasExtratiene un valor asignado, sumarlo aDiasPrestamoEstandary mostrar el resultado; si no tiene valor, mostrar soloDiasPrestamoEstandar. (Pista: puedes comprobardiasExtra.HasValuey acceder al valor condiasExtra.Value, o bien usar el operador??que asigna un valor por defecto cuando algo esnull:diasExtra ?? 0).
Soluciones
-
Con tipos explícitos:
string tituloLibro = "Rayuela"; int numeroPaginas = 635; bool estaDisponible = false; Console.WriteLine(tituloLibro); Console.WriteLine(numeroPaginas); Console.WriteLine(estaDisponible);Con
var(mismo resultado, tipo inferido automáticamente):var tituloLibro = "Rayuela"; var numeroPaginas = 635; var estaDisponible = false; Console.WriteLine(tituloLibro); Console.WriteLine(numeroPaginas); Console.WriteLine(estaDisponible);
string entrada = "350";
if (int.TryParse(entrada, out int paginas))
{
Console.WriteLine($"Numero de paginas registrado: {paginas}");
}
else
{
Console.WriteLine("El valor introducido no es un numero de paginas valido.");
}
Si cambiamos entrada a "trescientos", TryParse devolverá false y se ejecutará la
rama del else, mostrando el mensaje de error, sin que el programa se detenga de forma
abrupta (a diferencia de lo que ocurriría con int.Parse("trescientos"), que lanzaría una
excepción).
const int DiasPrestamoEstandar = 15;
int? diasExtra = null;
int totalDias = DiasPrestamoEstandar + (diasExtra ?? 0);
Console.WriteLine($"Dias totales de prestamo: {totalDias}");
diasExtra = 5; // ahora sí se asigna una ampliación
totalDias = DiasPrestamoEstandar + (diasExtra ?? 0);
Console.WriteLine($"Dias totales de prestamo (con ampliacion): {totalDias}");
El operador ?? (llamado operador de fusión con null) devuelve el valor de la izquierda
si no es null, o el de la derecha en caso contrario. Así, cuando diasExtra es null, se
suma 0; cuando tiene un valor, se suma ese valor.
Conclusión
En esta lección has aprendido a declarar variables con tipos explícitos y con var, a
distinguir entre tipos de valor y tipos de referencia, a convertir datos de un tipo a otro de
forma segura, a definir constantes, a entender el ámbito de una variable y a dar tus primeros
pasos con la nulabilidad básica. Todos estos conceptos son la base sobre la que se apoyará
absolutamente todo el código que escribas en los próximos módulos, empezando por las clases
que definiremos en el Módulo 3 (Libro, Socio, Prestamo), que no son más que un conjunto
organizado de variables (llamadas ahí "campos" o "propiedades") y de comportamiento asociado.
En la siguiente y última lección de este módulo, Arrays y Cadenas de Texto, aprenderás a
trabajar con colecciones de datos (por ejemplo, varios títulos de libros a la vez) usando
arrays, y a manipular texto en profundidad con la clase string, incluyendo interpolación,
búsquedas y transformaciones habituales.
Curso de Programación en C#
Módulo 1: Introducción a C#
- Introducción a C#
- Configuración del Entorno de Desarrollo
- Programa Hola Mundo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
- Arrays y Cadenas de Texto
Módulo 2: Estructuras de Control
Módulo 3: Programación Orientada a Objetos
- Clases y Objetos
- Métodos
- Constructores y Destructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
- Structs y Records: Tipos por Valor y por Referencia
Módulo 4: Conceptos Avanzados de C#
- Interfaces
- Delegados y Eventos
- Pattern Matching y Características Modernas de C#
- Genéricos
- Colecciones
- LINQ (Consulta Integrada en el Lenguaje)
- Programación Asíncrona
Módulo 5: Trabajando con Datos
- Entrada/Salida de Archivos
- Serialización
- Conectividad con Bases de Datos
- Entity Framework
- Trabajo con JSON y Consumo de APIs REST
Módulo 6: Temas Avanzados
- Reflexión
- Atributos
- Programación Dinámica
- Gestión de Memoria y Recolección de Basura
- Multihilo y Programación Paralela
Módulo 7: Construcción de Aplicaciones
- Formularios de Windows
- WPF (Windows Presentation Foundation)
- ASP.NET Core
- Blazor
- Xamarin y .NET MAUI
Módulo 8: Mejores Prácticas y Patrones de Diseño
- Estándares de Codificación y Mejores Prácticas
- Patrones de Diseño
- Inyección de Dependencias e Inversión de Control
- Pruebas Unitarias
- Revisión y Refactorización de Código
