Las clases en C# se definen de forma parecida a los registros (struct), sólo que ahora, además de variables (que representan sus detalles internos, y que llamaremos sus "atributos"), también incluirán funciones (las acciones que puede realizar ese objeto, que llamaremos sus "métodos"). Atributos y métodos formarán parte de "un todo", en vez de estar separados en distintas partes del programa. Esto es lo que se conoce como "Encapsulación".
Así, una clase "Puerta" se podría declarar así:
public class Puerta
{
int ancho; // Ancho en centimetros
int alto; // Alto en centimetros
int color; // Color en formato RGB
bool abierta; // Abierta o cerrada
public void Abrir()
{
abierta = true;
}
public void Cerrar()
{
abierta = false;
}
public void MostrarEstado()
{
Console.WriteLine("Ancho: {0}", ancho);
Console.WriteLine("Alto: {0}", alto);
Console.WriteLine("Color: {0}", color);
Console.WriteLine("Abierta: {0}", abierta);
}
} // Final de la clase Puerta
Como se puede observar, los objetos de la clase "Puerta" tendrán un ancho, un alto, un color, y un estado (abierta o no abierta), y además se podrán abrir o cerrar (y además, nos pueden "mostrar su estado", para comprobar que todo funciona correctamente).
Para declarar estos objetos que pertenecen a la clase "Puerta", usaremos la palabra "new", igual que hacíamos con los "arrays":
ejemplo = new int[4];
Cuando sepamos cuantos datos vamos a guardar (por ejemplo 4), podremos reservar espacio con la orden "new", así:
Puerta p = new Puerta();
p.Abrir();
p.MostrarEstado();
Vamos a completar un programa de prueba que use un objeto de esta clase (una "Puerta"), muestre su estado, la abra y vuelva a mostrar su estado:
// Ejemplo_06_02a.cs
// Primer ejemplo de clases
// Introducción a C#, por Nacho Cabanes
using System;
public class Puerta
{
int ancho; // Ancho en centimetros
int alto; // Alto en centimetros
int color; // Color en formato RGB
bool abierta; // Abierta o cerrada
public void Abrir()
{
abierta = true;
}
public void Cerrar()
{
abierta = false;
}
public void MostrarEstado()
{
Console.WriteLine("Ancho: {0}", ancho);
Console.WriteLine("Alto: {0}", alto);
Console.WriteLine("Color: {0}", color);
Console.WriteLine("Abierta: {0}", abierta);
}
} // Final de la clase Puerta
public class Ejemplo_06_02a
{
public static void Main()
{
Puerta p = new Puerta();
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.MostrarEstado();
}
}
Este fuente ya no contiene una única clase (class), como todos nuestros ejemplos anteriores, sino dos clases distintas:
(Nota: al compilar, puede que obtengas algún "Aviso" -warning- que te dice que has declarado "alto", "ancho" y "color", pero no las estás utilizando; no es importante por ahora, puedes ignorar ese aviso).
El resultado de ese programa es el siguiente:
Valores iniciales...
Ancho: 0
Alto: 0
Color: 0
Abierta: False
Vamos a abrir...
Ancho: 0
Alto: 0
Color: 0
Abierta: True
Se puede ver que en C# (pero no en todos los lenguajes), las variables que forman parte de una clase (los "atributos") tienen un valor inicial predefinido: 0 para los números, una cadena vacía para las cadenas de texto, "false" para los datos booleanos.
Vemos también que se accede a los métodos y a los datos precediendo el nombre de cada uno por el nombre de la variable y por un punto, como hacíamos con los registros (struct).
Aun así, en nuestro caso no podemos hacer directamente "p.abierta = true" desde el programa principal, por dos motivos:
Por ejemplo, para conocer y modificar los valores del "ancho" de una puerta, podríamos crear un método LeerAncho, que nos devolviera su valor, y un método CambiarAncho, que lo reemplazase por otro valor. No hay un convenio claro sobre cómo llamar a a estos métodos en español, por lo que es frecuente usar las palabras inglesas "Get" y "Set" para leer y cambiar un valor, respectivamente. Así, crearemos funciones auxiliares GetXXX y SetXXX que permitan acceder al valor de los atributos (en C# existe una forma alternativa de hacerlo, usando "propiedades", que veremos más adelante):
public int GetAncho()
{
return ancho;
}
public void SetAncho(int nuevoValor)
{
ancho = nuevoValor;
}
Así, una nueva versión del programa, que incluya ejemplos de Get y Set, podría ser:
// Ejemplo_06_02b.cs
// Clases, get y set
// Introducción a C#, por Nacho Cabanes
using System;
public class Puerta
{
int ancho; // Ancho en centimetros
int alto; // Alto en centimetros
int color; // Color en formato RGB
bool abierta; // Abierta o cerrada
public void Abrir()
{
abierta = true;
}
public void Cerrar()
{
abierta = false;
}
public int GetAncho()
{
return ancho;
}
public void SetAncho(int nuevoValor)
{
ancho = nuevoValor;
}
public void MostrarEstado()
{
Console.WriteLine("Ancho: {0}", ancho);
Console.WriteLine("Alto: {0}", alto);
Console.WriteLine("Color: {0}", color);
Console.WriteLine("Abierta: {0}", abierta);
}
} // Final de la clase Puerta
public class Ejemplo_06_02b
{
public static void Main()
{
Puerta p = new Puerta();
Console.WriteLine("Valores iniciales...");
p.MostrarEstado();
Console.WriteLine();
Console.WriteLine("Vamos a abrir...");
p.Abrir();
p.SetAncho(80);
p.MostrarEstado();
}
}
También puede desconcertar que en "Main" aparezca la palabra "static", mientras que no lo hace en los métodos de la clase "Puerta". Veremos el motivo un poco más adelante, pero de momento perderemos la costumbre de escribir "static" antes de cada función: a partir de ahora, sólo Main será "static".
Ejercicios propuestos:
Ejercicio propuesto 6.2.1: Crea una clase llamada Persona, en el fichero "persona.cs". Esta clase deberá tener un atributo "nombre", de tipo string. También deberá tener un método "SetNombre", de tipo void y con un parámetro string, que permita cambiar el valor del nombre. Finalmente, también tendrá un método "Saludar", que escribirá en pantalla "Hola, soy " seguido de su nombre. Crea también una clase llamada PruebaPersona. Esta clase deberá contener sólo la función Main, que creará dos objetos de tipo Persona, les asignará un nombre a cada uno y les pedirá que saluden.
Ejercicio propuesto 6.2.2: Tras leer la descripción de Space Invaders que vimos en el apartado anterior, crea una clase Juego,que sólo contenga un método Lanzar, void, sin parámetros, que escriba en pantalla "Bienvenido a Console Invaders. Pulse Intro para salir" y se parará hasta que el usuario pulse Intro. Prepara también un Main (en la misma clase), que cree un objeto de la clase juego y lo lance.
Ejercicio propuesto 6.2.3: Para guardar información sobre libros, vamos a comenzar por crear una clase "Libro", que contendrá atributos "autor", "titulo", "ubicacion" (todos ellos strings) y métodos Get y Set adecuados para leer su valor y cambiarlo. Prepara también un Main (en la misma clase), que cree un objeto de la clase Libro, dé valores a sus tres atributos y luego los muestre.
Ejercicio propuesto 6.2.4: Crea una clase "Coche", con atributos "marca" (texto), "modelo" (texto), "cilindrada" (número entero), potencia (número real). No hace falta que crees un Main de prueba.