Material de apoyo para estudiantes de segundo semestre de Ingeniería en Sistemas Computacionales del Instituto Tecnológico Superior de Lerdo. Aquí el temario AED-1286 está organizado por unidades, con explicación paso a paso, ejemplos básicos en C# Windows Forms, ejercicios tipo examen y dinámicas de clase. La idea es que el alumno no solo copie código: que entienda qué hace, por qué funciona y cuándo conviene usarlo.
Elige una unidad para ir directo al tema. Así la página se siente menos pared de texto y más tablero de control.
Introducción a POO, arreglo de ideas, clase, objeto y diagrama UML.
Clases, objetos, this, métodos, constructores, sobrecarga y operadores.
Herencia, reutilización y redefinición de métodos en clases derivadas.
Polimorfismo, clases abstractas, interfaces y variables polimórficas.
Excepciones, control de errores y creación de excepciones personalizadas.
Archivos de texto y binarios, lectura, escritura y persistencia.
Aquí no solo aparece el “qué” del tema, sino también el “para qué” y el “cómo”. La idea es que el alumno primero entienda la lógica, después vea el código y por último practique con problemas sencillos. Porque aventarles puro código sin contexto es como enseñar a manejar empezando en periférico a las 8 de la mañana: técnicamente posible, pedagógicamente cuestionable.
Antes de entrar de lleno a POO, conviene recordar que un arreglo es una estructura que guarda varios elementos del mismo tipo. En POO se siguen usando mucho, por ejemplo para almacenar calificaciones, nombres o cantidades antes de encapsularlos en clases más complejas. Un arreglo no “piensa”; solo guarda posiciones. La clase, en cambio, organiza datos y comportamiento.
private void btnPromedio_Click(object sender, EventArgs e)
{
double[] calificaciones = { 90, 85, 100, 78, 95 };
double suma = 0;
for (int i = 0; i < calificaciones.Length; i++)
{
suma += calificaciones[i];
}
double promedio = suma / calificaciones.Length;
MessageBox.Show("Promedio: " + promedio);
}
La POO modela problemas del mundo real usando objetos. Un objeto tiene características y acciones. Por ejemplo, un alumno tiene nombre, matrícula y semestre; además puede inscribirse, estudiar o mostrar sus datos. La clase es el molde; el objeto es el elemento real creado a partir de ese molde.
Es la plantilla. Define qué datos tendrá el objeto y qué puede hacer.
Es la instancia creada a partir de la clase. Ya existe en memoria y puede usarse.
Consiste en quedarse con lo importante del problema y descartar lo irrelevante.
Divide el programa en partes más pequeñas y organizadas para facilitar mantenimiento.
Protege los datos y controla cómo se modifican.
Permiten reutilizar y adaptar el comportamiento sin rehacer código desde cero.
Si se modela una Computadora, no hace falta describir cada tornillo. Basta con atributos como marca, memoria RAM y procesador; y métodos como encender o mostrar información. Eso es abstracción: representar lo esencial sin ahogarse en detalles.
public class Computadora
{
public string Marca;
public int Ram;
public void MostrarDatos()
{
MessageBox.Show("Marca: " + Marca + "\nRAM: " + Ram + " GB");
}
}
private void btnMostrar_Click(object sender, EventArgs e)
{
Computadora pc = new Computadora();
pc.Marca = "Lenovo";
pc.Ram = 16;
pc.MostrarDatos();
}
Antes de programar conviene dibujar el problema. El diagrama de clases de UML permite representar visualmente una clase con sus atributos y métodos. Esto ayuda al alumno a pensar antes de escribir código, que suele ser una habilidad poco glamorosa pero extremadamente útil.
En clase puedes pedirles primero el diagrama y luego el código. Así evitas el clásico “profe, ya hice el formulario, pero no sé qué hace”.
Problema: Diseña una clase Libro con los atributos título, autor y número de páginas. Crea un botón que instancie un objeto y muestre la información en un MessageBox.
Divide al grupo en equipos. Cada equipo debe elegir un objeto real del salón o del campus: computadora, mochila, proyector, credencial, etc. Deben escribir en una hoja: clase, tres atributos, tres métodos y un pequeño diagrama UML. Después explican por qué esos elementos son importantes. Gana el equipo que modele mejor el objeto sin meter atributos absurdos como “olor a taquito de recreo”.
Declarar una clase significa definir su estructura. Los atributos guardan información y los métodos ejecutan acciones. El encapsulamiento protege los datos para que no se modifiquen sin control. En otras palabras: no todo mundo debe entrar a la cocina del programa y cambiarle la sal.
public class Circulo
{
private double radio;
public void AsignarRadio(double r)
{
if (r > 0)
radio = r;
}
public double CalcularArea()
{
return Math.PI * radio * radio;
}
}
private void btnArea_Click(object sender, EventArgs e)
{
Circulo c = new Circulo();
c.AsignarRadio(double.Parse(txtRadio.Text));
lblResultado.Text = "Área: " + c.CalcularArea().ToString("0.00");
}
Una clase por sí sola no hace nada hasta que se crea un objeto. A ese proceso se le llama instanciación. Cuando escribimos
Alumno a = new Alumno(); estamos reservando memoria para un objeto que ya puede usarse.
La palabra reservada this hace referencia al objeto actual. Se usa mucho cuando el nombre del parámetro coincide con el del atributo. Sin this, C# se puede confundir; y el compilador no adivina intenciones, aunque a veces el alumno crea que sí.
public class Alumno
{
private string nombre;
public void AsignarNombre(string nombre)
{
this.nombre = nombre;
}
}
Un método es una acción que realiza el objeto. Puede recibir datos (parámetros) y devolver resultados (return). También puede ejecutarse sin devolver nada, como cuando muestra un mensaje.
Solo ejecuta una acción, por ejemplo mostrar información.
Devuelve un valor, por ejemplo un cálculo.
public class Operacion
{
public double Sumar(double a, double b)
{
return a + b;
}
}
El constructor es un método especial que se ejecuta al crear el objeto. Sirve para inicializar atributos. Los destructores existen en C#, pero en aplicaciones básicas de Windows Forms casi no se usan de forma directa porque el recolector de basura se encarga de liberar memoria. Aun así, es importante que el alumno sepa que existen.
public class Rectangulo
{
private double baseR;
private double altura;
public Rectangulo(double baseR, double altura)
{
this.baseR = baseR;
this.altura = altura;
}
public double Area()
{
return baseR * altura;
}
}
La sobrecarga permite tener varios métodos con el mismo nombre, siempre que cambien los parámetros. Es útil cuando la operación es conceptualmente la misma, pero puede recibir diferentes datos.
public class Calculadora
{
public int Sumar(int a, int b)
{
return a + b;
}
public double Sumar(double a, double b)
{
return a + b;
}
}
C# permite redefinir algunos operadores para trabajar con objetos. Aunque es un tema más delicado, se puede introducir con ejemplos pequeños. La intención es que el alumno entienda que un objeto puede comportarse de forma personalizada.
public class Punto
{
public int X;
public int Y;
public static Punto operator +(Punto a, Punto b)
{
return new Punto { X = a.X + b.X, Y = a.Y + b.Y };
}
}
Problema: Desarrolla una aplicación Windows Forms que calcule el área de un círculo usando una clase, un constructor y un método que regrese el resultado. Agrega validación para que el radio no sea negativo.
Entrega tarjetas separadas con nombres de atributos, métodos, constructores y objetos. Los equipos deben unirlas para formar una clase correcta. Después escriben el código completo en el pizarrón o en Visual Studio. Es una gran forma de detectar quién entendió el tema y quién solo venía sobreviviendo por instinto.
La herencia permite crear una clase nueva a partir de otra existente. La clase original se llama base y la nueva se llama derivada. Esto evita repetir atributos y métodos cuando varias clases comparten características.
public class Persona
{
public string Nombre;
public int Edad;
}
public class Alumno : Persona
{
public string Carrera;
}
En C# se maneja de forma directa la herencia simple: una clase deriva de una sola clase base. La llamada “herencia múltiple” no se implementa con clases como tal, pero se resuelve parcialmente usando interfaces. Esto es importante aclararlo para evitar confusiones desde temprano.
Cuando una clase hereda de otra, puede usar sus atributos y métodos. Así, el código se vuelve más limpio y fácil de mantener. Por ejemplo, si Alumno hereda de Persona, ya no hace falta volver a declarar Nombre y Edad.
La palabra reservada base permite acceder a miembros de la clase padre. Se usa mucho en constructores y en métodos redefinidos.
public class Persona
{
public string Nombre;
public Persona(string nombre)
{
Nombre = nombre;
}
}
public class Alumno : Persona
{
public string Carrera;
public Alumno(string nombre, string carrera) : base(nombre)
{
Carrera = carrera;
}
}
Cuando se crea un objeto derivado, primero se ejecuta el constructor de la clase base y luego el de la derivada. Esa secuencia ayuda a inicializar correctamente el objeto completo. En nivel básico basta con que el alumno comprenda el orden y sepa pasar datos desde la clase hija a la clase padre.
Si una clase hija necesita modificar el comportamiento heredado, puede redefinir métodos usando virtual y override. Así, la estructura general se conserva, pero la acción concreta cambia según el tipo de objeto.
public class Animal
{
public virtual string HacerSonido()
{
return "Sonido genérico";
}
}
public class Perro : Animal
{
public override string HacerSonido()
{
return "Guau guau";
}
}
Problema: Crea una clase base Empleado con nombre y sueldo. Deriva una clase Docente con especialidad. Muestra los datos en un formulario y agrega un método redefinido para describir el puesto.
En equipos, eligen una familia de objetos: vehículos, animales, empleados, dispositivos, videojuegos, etc. Deben construir su árbol de herencia en cartulina o en diapositiva. Luego explican qué se hereda y qué cambia en cada clase hija. La meta es que vean la herencia como organización lógica, no como adorno sintáctico.
El polimorfismo permite que una misma referencia trabaje con objetos de distintos tipos relacionados. Dicho más fácil: varios objetos pueden responder al mismo mensaje, pero cada uno a su manera. No es magia; es diseño bien hecho.
Una clase abstracta sirve como base general para otras clases, pero no se instancia directamente. Puede tener métodos normales y métodos abstractos, que obligan a las clases hijas a implementarlos. Es útil cuando todas las clases comparten una idea, pero cada una resuelve una acción de forma distinta.
public abstract class Figura
{
public abstract double CalcularArea();
}
public class Cuadrado : Figura
{
public double Lado;
public override double CalcularArea()
{
return Lado * Lado;
}
}
Una interfaz define un contrato: indica qué métodos debe implementar una clase. No guarda implementación, solo la promesa. Sirve mucho para organizar soluciones flexibles y para introducir al alumno a diseños más limpios.
public interface IImprimible
{
string Imprimir();
}
public class Ticket : IImprimible
{
public string Imprimir()
{
return "Ticket generado correctamente";
}
}
Una variable polimórfica es una referencia de una clase base que puede apuntar a objetos derivados. Por ejemplo, una referencia de tipo Figura puede apuntar a un Cuadrado, un Círculo o un Triángulo. Así se escribe código más general y reutilizable.
Figura f;
f = new Cuadrado();
MessageBox.Show("Objeto actual: Cuadrado");
La reutilización es una consecuencia natural de herencia, abstracción, interfaces y polimorfismo. No se trata solo de “copiar menos”, sino de diseñar mejor. Cuando el alumno entiende esto, deja de hacer cinco formularios iguales con cinco códigos casi idénticos y empieza a ahorrar tiempo, errores y café.
Problema: Diseña una clase abstracta Figura con el método abstracto CalcularArea(). Crea dos clases derivadas: Rectangulo y Triangulo. En el formulario, captura datos y muestra el área según el objeto seleccionado.
Cada equipo recibe una acción común, por ejemplo MostrarInfo() o Calcular(). Deben diseñar tres clases que respondan distinto a esa misma acción. Después presentan sus ejemplos. Esto aterriza el polimorfismo mucho mejor que memorizar una definición que suena a hechizo de Harry Potter versión programador.
Una excepción es un error que ocurre en tiempo de ejecución. No siempre significa que el programa esté mal planteado; a veces solo indica que el usuario escribió algo incorrecto, intentó dividir entre cero o quiso abrir un archivo que no existe. En vez de dejar que la aplicación se rompa feo, se controlan con bloques try-catch.
Algunas frecuentes en ejercicios básicos son: FormatException (dato mal escrito), DivideByZeroException, NullReferenceException y IOException. Conviene que el alumno identifique no solo el nombre, sino qué situación la provoca.
Si una excepción no se maneja en el método donde ocurre, sube al método que lo llamó. A eso se le llama propagación. Es como una papa caliente digital: si nadie la atrapa, termina explotando en la aplicación.
El manejo se realiza con try, catch y opcionalmente finally. También se puede lanzar una excepción de forma manual con throw cuando detectamos una condición inválida.
private void btnDividir_Click(object sender, EventArgs e)
{
try
{
double a = double.Parse(txtA.Text);
double b = double.Parse(txtB.Text);
if (b == 0)
throw new Exception("No se puede dividir entre cero.");
lblResultado.Text = "Resultado: " + (a / b);
}
catch (FormatException)
{
MessageBox.Show("Captura números válidos.");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Cuando una regla de negocio es muy específica, se puede crear una excepción personalizada. Por ejemplo, impedir una calificación fuera de rango. Esto enseña al alumno que el manejo de errores también forma parte del diseño del software.
public class CalificacionInvalidaException : Exception
{
public CalificacionInvalidaException(string mensaje) : base(mensaje)
{
}
}
Problema: Crea una aplicación que lea dos números y permita dividirlos. Debe manejar errores de formato y división entre cero. Muestra mensajes claros al usuario y evita que el programa termine abruptamente.
Entrega a cada equipo pequeños fragmentos de código con errores ocultos. Deben identificar qué excepción ocurrirá, en qué línea y cómo corregirla. Al final explican su solución al grupo. Aquí muchos descubren que el problema no era Visual Studio “odiándolos”, sino un Parse mal usado.
Los archivos permiten guardar información de forma permanente. Mientras la aplicación está abierta, los datos viven en memoria; cuando se cierra, desaparecen. Para que persistan, hay que escribirlos en un archivo. Aquí el alumno entiende por fin por qué no todo puede vivir felizmente en un TextBox.
Los archivos de texto guardan contenido legible, como nombres o mensajes. Los binarios guardan datos en formato no legible directamente, pero suelen ser más eficientes para ciertos procesos. En nivel inicial conviene empezar con archivos de texto y luego mencionar los binarios como extensión natural.
Las operaciones más comunes son crear, escribir, leer, modificar y eliminar archivos. En C# se puede trabajar con clases como File, StreamWriter y StreamReader.
using System.IO;
private void btnGuardar_Click(object sender, EventArgs e)
{
File.WriteAllText("alumno.txt", txtNombre.Text);
MessageBox.Show("Archivo guardado correctamente");
}
using System.IO;
private void btnLeer_Click(object sender, EventArgs e)
{
if (File.Exists("alumno.txt"))
{
txtNombre.Text = File.ReadAllText("alumno.txt");
}
else
{
MessageBox.Show("El archivo no existe");
}
}
Persistir objetos significa guardar su información para recuperarla después. A nivel básico puede resolverse guardando atributos en texto. Más adelante, esto se conecta con serialización y bases de datos. En segundo semestre basta con que comprendan la idea: un objeto no tiene por qué morir cuando se cierra el programa.
Problema: Desarrolla una aplicación Windows Forms para registrar el nombre y carrera de un alumno. Debe guardar la información en un archivo de texto y luego recuperarla con otro botón.
Los equipos crean una mini aplicación que guarde datos simples de una biblioteca, una papelería o una agenda. Después deben cerrar y volver a abrir la app para comprobar que los datos se conservan. La dinámica ayuda a conectar el concepto de persistencia con problemas reales de software.
Este desarrollo se construyó tomando como base el programa de la asignatura Programación Orientada a Objetos (AED-1286), donde aparecen las unidades, subtemas, competencias y prácticas propuestas para el periodo enero-junio 2026.