Capítulo 10: Clases y objetos (conceptos básicos)
Capítulo 10: Clases y objetos (conceptos básicos)
Sección titulada «Capítulo 10: Clases y objetos (conceptos básicos)»Parte I - Sección 2: Programación desde cero - Nivel: Principiante
🎯 Objetivos de aprendizaje
Sección titulada «🎯 Objetivos de aprendizaje»Al finalizar este capítulo serás capaz de:
- Entender qué es una clase y qué es un objeto (plano vs instancia)
- Crear clases propias con propiedades y métodos
- Usar constructores para inicializar objetos
- Instanciar objetos con la palabra clave
new - Aplicar encapsulación básica con public/private
- Comprender los conceptos de POO necesarios para trabajar con EPLAN API
- Construir un sistema completo de gestión de componentes eléctricos usando POO
📋 Requisitos previos
Sección titulada «📋 Requisitos previos»Antes de comenzar este capítulo debes:
- ✅ Haber completado el Capítulo 9
- ✅ Entender funciones y métodos
- ✅ Saber trabajar con colecciones (List, Dictionary)
- ✅ Conocer tipos de datos básicos (string, int, double, bool)
🏆 Proyecto del capítulo
Sección titulada «🏆 Proyecto del capítulo»Sistema de Gestión de Componentes Eléctricos con POO
Al final de este capítulo habrás construido un sistema orientado a objetos completo para:
- Modelar componentes eléctricos (Contactor, Guardamotor, Motor)
- Crear circuitos con múltiples componentes
- Calcular parámetros eléctricos (corriente total, potencia)
- Validar compatibilidad de componentes
- Generar reportes técnicos
Tiempo estimado: 70-80 minutos
⚠️ Nota Importante: En este capítulo, crearemos los objetos (contactores, motores, etc.) manualmente en el código instanciando las clases. Esto es intencional para comprender los fundamentos de la Programación Orientada a Objetos (POO). En capítulos posteriores, aprenderemos a crear estos objetos automáticamente a partir de los datos reales de EPLAN.
📚 Contenido
Sección titulada «📚 Contenido»1. Introducción: ¿Por qué necesitamos POO?
Sección titulada «1. Introducción: ¿Por qué necesitamos POO?»Hasta ahora has trabajado con variables individuales y colecciones. Pero, ¿cómo representamos un componente eléctrico completo con todos sus datos y comportamientos?
Enfoque sin POO (código difícil de mantener):
// Contactor K1string k1_designacion = "K1";string k1_codigo = "3RT2026-1BB40";double k1_corriente = 25.0;double k1_voltaje = 400.0;string k1_fabricante = "Siemens";
// Contactor K2string k2_designacion = "K2";string k2_codigo = "3RT2036-1BB40";double k2_corriente = 40.0;double k2_voltaje = 400.0;string k2_fabricante = "Siemens";
// ¿Y si necesitamos 100 contactores?// ¿Cómo agrupamos los datos de cada componente?// ¿Cómo calculamos la corriente total?Enfoque con POO (código organizado y escalable):
// Crear clase Contactorpublic class Contactor{ public string Designacion { get; set; } public string Codigo { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; } public string Fabricante { get; set; }
public string ObtenerDescripcion() { return string.Format("{0} - {1} ({2}A, {3}V)", Designacion, Codigo, Corriente, Voltaje); }}
// Crear objetos (instancias)Contactor k1 = new Contactor();k1.Designacion = "K1";k1.Codigo = "3RT2026-1BB40";k1.Corriente = 25.0;k1.Voltaje = 400.0;k1.Fabricante = "Siemens";
MessageBox.Show(k1.ObtenerDescripcion());// "K1 - 3RT2026-1BB40 (25A, 400V)"Ventajas de POO:
- Organización: Datos y funciones relacionados juntos
- Reutilización: Crea plantillas (clases) y úsalas múltiples veces
- Mantenibilidad: Cambios centralizados
- Escalabilidad: Fácil agregar nuevas funcionalidades
- Claridad: El código se lee como el mundo real
2. Conceptos fundamentales
Sección titulada «2. Conceptos fundamentales»2.1 Clase vs Objeto
Sección titulada «2.1 Clase vs Objeto»Clase: Es el plano o plantilla que define cómo será algo.
Objeto: Es una instancia concreta creada a partir de la clase.
Analogía con planos arquitectónicos:
Clase Contactor → Plano de un contactor (especificación) ↓ instanciarObjeto k1 → Contactor físico instalado en el tableroObjeto k2 → Otro contactor físico del mismo tipoEjemplo visual:
// CLASE: Plano (definición)public class Contactor{ // Propiedades (datos) public string Designacion { get; set; } public double Corriente { get; set; }
// Métodos (comportamientos) public void Activar() { MessageBox.Show(Designacion + " activado"); }}
// OBJETOS: Instancias concretasContactor k1 = new Contactor(); // Primer contactorContactor k2 = new Contactor(); // Segundo contactorContactor k3 = new Contactor(); // Tercer contactor
// Cada objeto tiene sus propios valoresk1.Designacion = "K1";k1.Corriente = 25.0;
k2.Designacion = "K2";k2.Corriente = 40.0;2.2 Anatomía de una clase
Sección titulada «2.2 Anatomía de una clase»public class Contactor{ // ===================================== // PROPIEDADES (Datos/Atributos) // ===================================== public string Designacion { get; set; } public string Codigo { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; }
// Propiedad privada (solo accesible dentro de la clase) private bool estaActivado;
// ===================================== // CONSTRUCTOR (Inicialización) // ===================================== public Contactor(string designacion, double corriente) { this.Designacion = designacion; this.Corriente = corriente; this.estaActivado = false; }
// ===================================== // MÉTODOS (Comportamientos/Funciones) // ===================================== public void Activar() { estaActivado = true; MessageBox.Show(Designacion + " activado"); }
public double CalcularPotencia(double voltaje) { return Corriente * voltaje; }
public string ObtenerEstado() { return estaActivado ? "Activado" : "Desactivado"; }}3. Propiedades
Sección titulada «3. Propiedades»Las propiedades almacenan los datos de un objeto.
3.1 Propiedades automáticas (más común)
Sección titulada «3.1 Propiedades automáticas (más común)»public class Componente{ // Sintaxis simplificada public string Designacion { get; set; } public double Corriente { get; set; } public string Fabricante { get; set; }}
// UsoComponente comp = new Componente();comp.Designacion = "K1"; // setstring nombre = comp.Designacion; // getExplicación:
{ get; set; }crea automáticamente getters y settersget: Permite leer el valorset: Permite escribir el valor
3.2 Propiedades de solo lectura
Sección titulada «3.2 Propiedades de solo lectura»public class Componente{ // Solo se puede leer, no modificar desde fuera public string Codigo { get; private set; }
public Componente(string codigo) { this.Codigo = codigo; // Solo se asigna en el constructor }}
// UsoComponente comp = new Componente("3RT2026-1BB40");MessageBox.Show(comp.Codigo); // ✓ OK - lectura permitidacomp.Codigo = "otro"; // ❌ ERROR - escritura no permitida3.3 Propiedades calculadas
Sección titulada «3.3 Propiedades calculadas»public class Motor{ public double Potencia { get; set; } // kW public double Voltaje { get; set; } // V public double FactorPotencia { get; set; } // cosφ
// Propiedad calculada (sin almacenar valor) public double CorrienteNominal { get { // I = P / (√3 × V × cosφ) return (Potencia * 1000) / (1.732 * Voltaje * FactorPotencia); } }}
// UsoMotor m1 = new Motor();m1.Potencia = 7.5; // kWm1.Voltaje = 400.0; // Vm1.FactorPotencia = 0.85; // cosφ
double corriente = m1.CorrienteNominal; // Se calcula automáticamenteMessageBox.Show("Corriente nominal: " + corriente.ToString("F2") + "A");// "Corriente nominal: 12.77A"3.4 Valores por defecto
Sección titulada «3.4 Valores por defecto»public class Guardamotor{ public string Designacion { get; set; } public double Corriente { get; set; }
// Valores por defecto public string Tipo { get; set; } = "Guardamotor"; public int Polos { get; set; } = 3; public bool ConAuxiliar { get; set; } = false;}
// UsoGuardamotor q1 = new Guardamotor();MessageBox.Show(q1.Tipo); // "Guardamotor"MessageBox.Show(q1.Polos.ToString()); // "3"4. Constructores
Sección titulada «4. Constructores»El constructor es un método especial que se ejecuta al crear un objeto con new.
4.1 Constructor sin parámetros (por defecto)
Sección titulada «4.1 Constructor sin parámetros (por defecto)»public class Contactor{ public string Designacion { get; set; } public double Corriente { get; set; }
// Constructor vacío (opcional, se crea automáticamente si no lo defines) public Contactor() { // Inicialización vacía }}
// UsoContactor k1 = new Contactor();k1.Designacion = "K1";k1.Corriente = 25.0;4.2 Constructor con parámetros (recomendado)
Sección titulada «4.2 Constructor con parámetros (recomendado)»public class Contactor{ public string Designacion { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; }
// Constructor con parámetros public Contactor(string designacion, double corriente, double voltaje) { this.Designacion = designacion; this.Corriente = corriente; this.Voltaje = voltaje; }}
// Uso (más limpio y seguro)Contactor k1 = new Contactor("K1", 25.0, 400.0);MessageBox.Show(k1.Designacion); // "K1"La palabra clave this:
this.Designacionse refiere a la propiedad del objetodesignacion(sin this) se refiere al parámetro del constructor
4.3 Múltiples constructores (sobrecarga)
Sección titulada «4.3 Múltiples constructores (sobrecarga)»public class Contactor{ public string Designacion { get; set; } public string Codigo { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; }
// Constructor 1: Solo designación y corriente public Contactor(string designacion, double corriente) { this.Designacion = designacion; this.Corriente = corriente; this.Voltaje = 400.0; // Valor por defecto }
// Constructor 2: Todos los parámetros public Contactor(string designacion, string codigo, double corriente, double voltaje) { this.Designacion = designacion; this.Codigo = codigo; this.Corriente = corriente; this.Voltaje = voltaje; }}
// Uso de diferentes constructoresContactor k1 = new Contactor("K1", 25.0);Contactor k2 = new Contactor("K2", "3RT2036-1BB40", 40.0, 400.0);4.4 Llamar a otro constructor (this)
Sección titulada «4.4 Llamar a otro constructor (this)»public class Contactor{ public string Designacion { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; }
// Constructor principal public Contactor(string designacion, double corriente, double voltaje) { this.Designacion = designacion; this.Corriente = corriente; this.Voltaje = voltaje; }
// Constructor simplificado que llama al principal public Contactor(string designacion, double corriente) : this(designacion, corriente, 400.0) // Llama al constructor principal { // Voltaje = 400V por defecto }}5. Métodos
Sección titulada «5. Métodos»Los métodos son las funciones que definen el comportamiento de un objeto.
5.1 Métodos sin retorno (void)
Sección titulada «5.1 Métodos sin retorno (void)»public class Contactor{ public string Designacion { get; set; } private bool estaActivado;
public void Activar() { estaActivado = true; MessageBox.Show(Designacion + " activado"); }
public void Desactivar() { estaActivado = false; MessageBox.Show(Designacion + " desactivado"); }}
// UsoContactor k1 = new Contactor();k1.Designacion = "K1";k1.Activar(); // Muestra mensajek1.Desactivar(); // Muestra mensaje5.2 Métodos con retorno
Sección titulada «5.2 Métodos con retorno»public class Motor{ public double Potencia { get; set; } // kW public double Voltaje { get; set; } // V
// Método que retorna un valor public double CalcularCorriente(double factorPotencia) { // I = P / (√3 × V × cosφ) double corriente = (Potencia * 1000) / (1.732 * Voltaje * factorPotencia); return corriente; }
public string ObtenerClasificacion() { if (Potencia < 1.0) return "Motor pequeño"; else if (Potencia < 10.0) return "Motor mediano"; else return "Motor grande"; }}
// UsoMotor m1 = new Motor();m1.Potencia = 7.5;m1.Voltaje = 400.0;
double corriente = m1.CalcularCorriente(0.85);MessageBox.Show("Corriente: " + corriente.ToString("F2") + "A");
string clasificacion = m1.ObtenerClasificacion();MessageBox.Show("Clasificación: " + clasificacion); // "Motor mediano"5.3 Métodos con parámetros opcionales
Sección titulada «5.3 Métodos con parámetros opcionales»public class Componente{ public string Designacion { get; set; } public double Corriente { get; set; }
// Parámetros opcionales con valores por defecto public string GenerarEtiqueta(bool incluirCorriente = true, string prefijo = "") { string etiqueta = prefijo + Designacion;
if (incluirCorriente) { etiqueta += " (" + Corriente + "A)"; }
return etiqueta; }}
// UsoComponente c = new Componente();c.Designacion = "K1";c.Corriente = 25.0;
MessageBox.Show(c.GenerarEtiqueta()); // "K1 (25A)"MessageBox.Show(c.GenerarEtiqueta(false)); // "K1"MessageBox.Show(c.GenerarEtiqueta(true, "CONT-")); // "CONT-K1 (25A)"MessageBox.Show(c.GenerarEtiqueta(prefijo: "CONT-")); // "CONT-K1 (25A)"6. Encapsulación: public vs private
Sección titulada «6. Encapsulación: public vs private»La encapsulación es el principio de ocultar detalles internos y exponer solo lo necesario.
6.1 Modificadores de acceso
Sección titulada «6.1 Modificadores de acceso»public class Contactor{ // PUBLIC: Accesible desde fuera de la clase public string Designacion { get; set; } public double Corriente { get; set; }
// PRIVATE: Solo accesible dentro de la clase private bool estaActivado; private int contadorActivaciones;
public void Activar() { estaActivado = true; // ✓ OK - acceso interno contadorActivaciones++; // ✓ OK - acceso interno MessageBox.Show(Designacion + " activado"); }
public int ObtenerContadorActivaciones() { return contadorActivaciones; // ✓ OK - retornar valor privado }}
// UsoContactor k1 = new Contactor();k1.Designacion = "K1"; // ✓ OK - publick1.Activar(); // ✓ OK - public
int contador = k1.ObtenerContadorActivaciones(); // ✓ OK - método publicMessageBox.Show("Activaciones: " + contador);
// k1.estaActivado = true; // ❌ ERROR - private no accesible// k1.contadorActivaciones++; // ❌ ERROR - private no accesible6.2 ¿Por qué usar private?
Sección titulada «6.2 ¿Por qué usar private?»Ventajas de la encapsulación:
public class Motor{ public double Potencia { get; set; } // kW
// Corriente es privada y se calcula automáticamente private double corrienteNominal;
// Método público para establecer potencia y calcular corriente public void EstablecerPotencia(double potencia, double voltaje, double fp) { this.Potencia = potencia;
// Cálculo interno (encapsulado) this.corrienteNominal = (potencia * 1000) / (1.732 * voltaje * fp); }
// Método público para obtener corriente public double ObtenerCorriente() { return corrienteNominal; }}Beneficios:
- ✅ Protege datos sensibles
- ✅ Evita modificaciones incorrectas
- ✅ Centraliza lógica de validación
- ✅ Permite cambiar implementación interna sin afectar código externo
7. La palabra clave this
Sección titulada «7. La palabra clave this»this se refiere al objeto actual.
7.1 Distinguir entre parámetros y propiedades
Sección titulada «7.1 Distinguir entre parámetros y propiedades»public class Contactor{ public string Designacion { get; set; } public double Corriente { get; set; }
public Contactor(string Designacion, double Corriente) { // Sin this: ambigüedad // Designacion = Designacion; // ¿Cuál es cuál?
// Con this: claridad this.Designacion = Designacion; // this.Designacion = propiedad this.Corriente = Corriente; // Corriente = parámetro }}7.2 Pasar el objeto actual a otro método
Sección titulada «7.2 Pasar el objeto actual a otro método»public class Circuito{ private List<Contactor> contactores;
public void AgregarContactor(Contactor contactor) { contactores.Add(contactor); }}
public class Contactor{ public string Designacion { get; set; }
public void RegistrarEnCircuito(Circuito circuito) { circuito.AgregarContactor(this); // Pasar el objeto actual }}
// UsoCircuito circuito1 = new Circuito();Contactor k1 = new Contactor();k1.Designacion = "K1";k1.RegistrarEnCircuito(circuito1); // k1 se agrega a sí mismo al circuito💻 Manos a la obra: Construyendo el proyecto
Sección titulada «💻 Manos a la obra: Construyendo el proyecto»Vamos a crear un Sistema de Gestión de Componentes Eléctricos completo usando POO.
Paso 1: Clase base ComponenteElectrico
Sección titulada «Paso 1: Clase base ComponenteElectrico»public class ComponenteElectrico{ // ======================================== // PROPIEDADES // ======================================== public string Designacion { get; set; } public string Codigo { get; set; } public string Tipo { get; set; } public double Corriente { get; set; } // A public double Voltaje { get; set; } // V public string Fabricante { get; set; }
// Propiedad calculada public double Potencia { get { return Corriente * Voltaje; // P = I × V (aproximado) } }
// ======================================== // CONSTRUCTOR // ======================================== public ComponenteElectrico(string designacion, string codigo, string tipo, double corriente, double voltaje, string fabricante) { this.Designacion = designacion; this.Codigo = codigo; this.Tipo = tipo; this.Corriente = corriente; this.Voltaje = voltaje; this.Fabricante = fabricante; }
// Constructor simplificado public ComponenteElectrico(string designacion, double corriente) { this.Designacion = designacion; this.Corriente = corriente; this.Voltaje = 400.0; // Por defecto this.Tipo = "Genérico"; }
// ======================================== // MÉTODOS // ======================================== public string ObtenerDescripcionCompleta() { return string.Format("{0} [{1}]\n Código: {2}\n Corriente: {3}A\n Voltaje: {4}V\n Potencia: {5:F2}W\n Fabricante: {6}", Designacion, Tipo, Codigo ?? "N/A", Corriente, Voltaje, Potencia, Fabricante ?? "N/A"); }
public string ObtenerDescripcionCorta() { return string.Format("{0} - {1} ({2}A, {3}V)", Designacion, Tipo, Corriente, Voltaje); }
public bool EsCompatibleConVoltaje(double voltajeRequerido) { // Tolerancia del 10% double tolerancia = voltajeRequerido * 0.10; return Math.Abs(Voltaje - voltajeRequerido) <= tolerancia; }
public void MostrarInformacion() { MessageBox.Show(ObtenerDescripcionCompleta(), "Información del Componente"); }}¿Qué acabamos de hacer?
Creamos una clase base ComponenteElectrico que:
- Almacena datos eléctricos básicos (corriente, voltaje, código)
- Calcula potencia automáticamente
- Tiene dos constructores (completo y simplificado)
- Provee métodos para obtener descripciones formateadas
- Valida compatibilidad de voltaje
Paso 2: Clase Contactor (especializada)
Sección titulada «Paso 2: Clase Contactor (especializada)»public class Contactor : ComponenteElectrico{ // ======================================== // PROPIEDADES ESPECÍFICAS // ======================================== public int Polos { get; set; } public string CategoriaUso { get; set; } // AC1, AC3, AC4 private bool estaActivado; private int contadorActivaciones;
// ======================================== // CONSTRUCTOR // ======================================== public Contactor(string designacion, string codigo, double corrienteAC3, double voltaje, int polos, string fabricante) : base(designacion, codigo, "Contactor", corrienteAC3, voltaje, fabricante) { this.Polos = polos; this.CategoriaUso = "AC3"; this.estaActivado = false; this.contadorActivaciones = 0; }
// ======================================== // MÉTODOS ESPECÍFICOS // ======================================== public void Activar() { if (!estaActivado) { estaActivado = true; contadorActivaciones++; MessageBox.Show(string.Format("Contactor {0} ACTIVADO\nActivaciones totales: {1}", Designacion, contadorActivaciones)); } else { MessageBox.Show(Designacion + " ya está activado"); } }
public void Desactivar() { if (estaActivado) { estaActivado = false; MessageBox.Show(Designacion + " desactivado"); } }
public string ObtenerEstado() { return estaActivado ? "ACTIVADO" : "DESACTIVADO"; }
public int ObtenerContadorActivaciones() { return contadorActivaciones; }}Paso 3: Clase Circuito (contenedor)
Sección titulada «Paso 3: Clase Circuito (contenedor)»public class Circuito{ // ======================================== // PROPIEDADES // ======================================== public string Nombre { get; set; } public double VoltajeNominal { get; set; } private List<ComponenteElectrico> componentes;
// ======================================== // CONSTRUCTOR // ======================================== public Circuito(string nombre, double voltajeNominal) { this.Nombre = nombre; this.VoltajeNominal = voltajeNominal; this.componentes = new List<ComponenteElectrico>(); }
// ======================================== // MÉTODOS DE GESTIÓN // ======================================== public bool AgregarComponente(ComponenteElectrico componente) { // Validar compatibilidad de voltaje if (!componente.EsCompatibleConVoltaje(VoltajeNominal)) { MessageBox.Show(string.Format("Error: {0} no es compatible con {1}V\n(Voltaje del componente: {2}V)", componente.Designacion, VoltajeNominal, componente.Voltaje), "Voltaje incompatible"); return false; }
componentes.Add(componente); MessageBox.Show(string.Format("{0} agregado al circuito {1}", componente.Designacion, Nombre)); return true; }
public void EliminarComponente(string designacion) { ComponenteElectrico comp = componentes.Find(c => c.Designacion == designacion);
if (comp != null) { componentes.Remove(comp); MessageBox.Show(designacion + " eliminado del circuito"); } else { MessageBox.Show(designacion + " no encontrado"); } }
public ComponenteElectrico BuscarComponente(string designacion) { return componentes.Find(c => c.Designacion == designacion); }
// ======================================== // MÉTODOS DE CÁLCULO // ======================================== public double CalcularCorrienteTotal() { double total = 0; foreach (ComponenteElectrico comp in componentes) { total += comp.Corriente; } return total; }
public double CalcularPotenciaTotal() { double total = 0; foreach (ComponenteElectrico comp in componentes) { total += comp.Potencia; } return total; }
public int ObtenerCantidadComponentes() { return componentes.Count; }
// ======================================== // MÉTODOS DE REPORTE // ======================================== public void MostrarResumen() { string reporte = "=======================================\n"; reporte += string.Format("CIRCUITO: {0}\n", Nombre); reporte += string.Format("Voltaje nominal: {0}V\n", VoltajeNominal); reporte += "=======================================\n\n"; reporte += string.Format("Componentes: {0}\n", componentes.Count); reporte += string.Format("Corriente total: {0:F2}A\n", CalcularCorrienteTotal()); reporte += string.Format("Potencia total: {0:F2}W\n\n", CalcularPotenciaTotal());
reporte += "LISTA DE COMPONENTES:\n"; reporte += "---------------------------------------\n";
foreach (ComponenteElectrico comp in componentes) { reporte += comp.ObtenerDescripcionCorta() + "\n"; }
MessageBox.Show(reporte, "Resumen del Circuito"); }
public void MostrarDetalleCompleto() { string reporte = string.Format("CIRCUITO: {0} ({1}V)\n\n", Nombre, VoltajeNominal);
if (componentes.Count == 0) { reporte += "No hay componentes en este circuito."; } else { foreach (ComponenteElectrico comp in componentes) { reporte += comp.ObtenerDescripcionCompleta(); reporte += "\n\n---------------------------------------\n\n"; } }
MessageBox.Show(reporte, "Detalle Completo del Circuito"); }}Código completo del proyecto
Sección titulada «Código completo del proyecto»//// Proyecto: Sistema de Gestión de Componentes Eléctricos con POO// Capítulo: 10// Descripción: Sistema completo para gestionar componentes eléctricos usando// programación orientada a objetos (clases, propiedades, métodos,// constructores, encapsulación)// Autor: C.D. López// Fecha: Enero 2025//
using System;using System.Collections.Generic;using Eplan.EplApi.Base;using Eplan.EplApi.ApplicationFramework;
// ========================================// CLASE BASE: ComponenteElectrico// ========================================public class ComponenteElectrico{ // Propiedades public string Designacion { get; set; } public string Codigo { get; set; } public string Tipo { get; set; } public double Corriente { get; set; } public double Voltaje { get; set; } public string Fabricante { get; set; }
// Propiedad calculada public double Potencia { get { return Corriente * Voltaje; } }
// Constructores public ComponenteElectrico(string designacion, string codigo, string tipo, double corriente, double voltaje, string fabricante) { this.Designacion = designacion; this.Codigo = codigo; this.Tipo = tipo; this.Corriente = corriente; this.Voltaje = voltaje; this.Fabricante = fabricante; }
public ComponenteElectrico(string designacion, double corriente) { this.Designacion = designacion; this.Corriente = corriente; this.Voltaje = 400.0; this.Tipo = "Genérico"; }
// Métodos public string ObtenerDescripcionCompleta() { return string.Format("{0} [{1}]\n Código: {2}\n Corriente: {3}A\n Voltaje: {4}V\n Potencia: {5:F2}W\n Fabricante: {6}", Designacion, Tipo, Codigo ?? "N/A", Corriente, Voltaje, Potencia, Fabricante ?? "N/A"); }
public string ObtenerDescripcionCorta() { return string.Format("{0} - {1} ({2}A, {3}V)", Designacion, Tipo, Corriente, Voltaje); }
public bool EsCompatibleConVoltaje(double voltajeRequerido) { double tolerancia = voltajeRequerido * 0.10; return Math.Abs(Voltaje - voltajeRequerido) <= tolerancia; }
public void MostrarInformacion() { MessageBox.Show(ObtenerDescripcionCompleta(), "Información del Componente"); }}
// ========================================// CLASE ESPECIALIZADA: Contactor// ========================================public class Contactor : ComponenteElectrico{ public int Polos { get; set; } public string CategoriaUso { get; set; } private bool estaActivado; private int contadorActivaciones;
public Contactor(string designacion, string codigo, double corrienteAC3, double voltaje, int polos, string fabricante) : base(designacion, codigo, "Contactor", corrienteAC3, voltaje, fabricante) { this.Polos = polos; this.CategoriaUso = "AC3"; this.estaActivado = false; this.contadorActivaciones = 0; }
public void Activar() { if (!estaActivado) { estaActivado = true; contadorActivaciones++; MessageBox.Show(string.Format("Contactor {0} ACTIVADO\nActivaciones totales: {1}", Designacion, contadorActivaciones)); } else { MessageBox.Show(Designacion + " ya está activado"); } }
public void Desactivar() { if (estaActivado) { estaActivado = false; MessageBox.Show(Designacion + " desactivado"); } }
public string ObtenerEstado() { return estaActivado ? "ACTIVADO" : "DESACTIVADO"; }
public int ObtenerContadorActivaciones() { return contadorActivaciones; }}
// ========================================// CLASE CONTENEDOR: Circuito// ========================================public class Circuito{ public string Nombre { get; set; } public double VoltajeNominal { get; set; } private List<ComponenteElectrico> componentes;
public Circuito(string nombre, double voltajeNominal) { this.Nombre = nombre; this.VoltajeNominal = voltajeNominal; this.componentes = new List<ComponenteElectrico>(); }
public bool AgregarComponente(ComponenteElectrico componente) { if (!componente.EsCompatibleConVoltaje(VoltajeNominal)) { MessageBox.Show(string.Format("Error: {0} no es compatible con {1}V\n(Voltaje del componente: {2}V)", componente.Designacion, VoltajeNominal, componente.Voltaje), "Voltaje incompatible"); return false; }
componentes.Add(componente); MessageBox.Show(string.Format("{0} agregado al circuito {1}", componente.Designacion, Nombre)); return true; }
public void EliminarComponente(string designacion) { ComponenteElectrico comp = componentes.Find(c => c.Designacion == designacion);
if (comp != null) { componentes.Remove(comp); MessageBox.Show(designacion + " eliminado del circuito"); } else { MessageBox.Show(designacion + " no encontrado"); } }
public ComponenteElectrico BuscarComponente(string designacion) { return componentes.Find(c => c.Designacion == designacion); }
public double CalcularCorrienteTotal() { double total = 0; foreach (ComponenteElectrico comp in componentes) { total += comp.Corriente; } return total; }
public double CalcularPotenciaTotal() { double total = 0; foreach (ComponenteElectrico comp in componentes) { total += comp.Potencia; } return total; }
public int ObtenerCantidadComponentes() { return componentes.Count; }
public void MostrarResumen() { string reporte = "=======================================\n"; reporte += string.Format("CIRCUITO: {0}\n", Nombre); reporte += string.Format("Voltaje nominal: {0}V\n", VoltajeNominal); reporte += "=======================================\n\n"; reporte += string.Format("Componentes: {0}\n", componentes.Count); reporte += string.Format("Corriente total: {0:F2}A\n", CalcularCorrienteTotal()); reporte += string.Format("Potencia total: {0:F2}W\n\n", CalcularPotenciaTotal());
reporte += "LISTA DE COMPONENTES:\n"; reporte += "---------------------------------------\n";
foreach (ComponenteElectrico comp in componentes) { reporte += comp.ObtenerDescripcionCorta() + "\n"; }
MessageBox.Show(reporte, "Resumen del Circuito"); }
public void MostrarDetalleCompleto() { string reporte = string.Format("CIRCUITO: {0} ({1}V)\n\n", Nombre, VoltajeNominal);
if (componentes.Count == 0) { reporte += "No hay componentes en este circuito."; } else { foreach (ComponenteElectrico comp in componentes) { reporte += comp.ObtenerDescripcionCompleta(); reporte += "\n\n---------------------------------------\n\n"; } }
MessageBox.Show(reporte, "Detalle Completo del Circuito"); }}
// ========================================// SCRIPT PRINCIPAL DE DEMOSTRACIÓN// ========================================public class GestionComponentesPOO{ [Start] public void Ejecutar() { try { // Crear circuito Circuito circuitoPrincipal = new Circuito("Circuito Motor Principal", 400.0);
// Crear componentes Contactor k1 = new Contactor("K1", "3RT2026-1BB40", 25.0, 400.0, 3, "Siemens"); Contactor k2 = new Contactor("K2", "3RT2036-1BB40", 40.0, 400.0, 3, "Siemens");
ComponenteElectrico q1 = new ComponenteElectrico("Q1", "3RV2011-1JA10", "Guardamotor", 4.5, 400.0, "Siemens");
ComponenteElectrico m1 = new ComponenteElectrico("M1", 15.0); // Constructor simple m1.Tipo = "Motor"; m1.Fabricante = "WEG";
// Agregar componentes al circuito circuitoPrincipal.AgregarComponente(k1); circuitoPrincipal.AgregarComponente(k2); circuitoPrincipal.AgregarComponente(q1); circuitoPrincipal.AgregarComponente(m1);
// Mostrar resumen circuitoPrincipal.MostrarResumen();
// Activar contactores k1.Activar(); k1.Activar(); // Intento de activar de nuevo k2.Activar();
// Buscar y mostrar componente específico ComponenteElectrico componenteBuscado = circuitoPrincipal.BuscarComponente("Q1"); if (componenteBuscado != null) { componenteBuscado.MostrarInformacion(); }
// Mostrar detalle completo circuitoPrincipal.MostrarDetalleCompleto();
// Intentar agregar componente incompatible (220V en circuito 400V) ComponenteElectrico x1 = new ComponenteElectrico("X1", "XYZ-123", "Relé", 2.0, 220.0, "Generic"); circuitoPrincipal.AgregarComponente(x1); // Debería fallar
// Mostrar contadores de activaciones MessageBox.Show(string.Format("K1 activado {0} veces\nK2 activado {1} veces", k1.ObtenerContadorActivaciones(), k2.ObtenerContadorActivaciones()));
} catch (Exception ex) { MessageBox.Show("Error: " + ex.Message, "Error en la ejecución"); } }}🔍 Deep Dive: Entendiendo en profundidad
Sección titulada «🔍 Deep Dive: Entendiendo en profundidad»Herencia (adelanto)
Sección titulada «Herencia (adelanto)»En el código usamos herencia cuando Contactor : ComponenteElectrico.
public class Contactor : ComponenteElectrico{ // Hereda todas las propiedades y métodos de ComponenteElectrico // Puede agregar propiedades y métodos específicos}Beneficios:
- ✅ Reutilización de código
- ✅ Jerarquía lógica (especialización)
- ✅ Polimorfismo (veremos en capítulos avanzados)
Propiedades vs Campos
Sección titulada «Propiedades vs Campos»Campo (field):
private bool estaActivado; // Almacenamiento directoPropiedad (property):
public string Designacion { get; set; } // Con getters/setters automáticosCuándo usar cada uno:
- Propiedades públicas: Para datos que se acceden desde fuera
- Campos privados: Para datos internos que no se exponen
El operador ??
Sección titulada «El operador ??»Codigo ?? "N/A"Significa: “Si Codigo es null, usa "N/A" en su lugar”.
Mejores prácticas
Sección titulada «Mejores prácticas»- ✅ Hacer: Nombrar clases con sustantivos (
Contactor,Motor) - ✅ Hacer: Nombrar métodos con verbos (
Activar(),Calcular()) - ✅ Hacer: Usar constructores para inicializar objetos correctamente
- ✅ Hacer: Encapsular (private) datos internos
- ✅ Hacer: Validar datos en constructores y métodos públicos
- ❌ Evitar: Exponer todos los campos como public
- ❌ Evitar: Constructores sin validación
- ❌ Evitar: Métodos que hacen demasiadas cosas (responsabilidad única)
Errores comunes
Sección titulada «Errores comunes»Error 1: Olvidar new al crear objetos
Sección titulada «Error 1: Olvidar new al crear objetos»Síntoma: Error “Use of unassigned variable”
Causa: No instanciar el objeto
// ❌ MALContactor k1;k1.Designacion = "K1"; // ERROR: k1 no está inicializado
// ✓ BIENContactor k1 = new Contactor("K1", "3RT2026", 25.0, 400.0, 3, "Siemens");k1.Designacion = "K1"; // OKError 2: Confundir clase con objeto
Sección titulada «Error 2: Confundir clase con objeto»Síntoma: Error “An object reference is required…”
Causa: Intentar llamar métodos de instancia en la clase
// ❌ MALContactor.Activar(); // ERROR: Contactor es la clase, no un objeto
// ✓ BIENContactor k1 = new Contactor(...);k1.Activar(); // OK: k1 es un objetoError 3: NullReferenceException
Sección titulada «Error 3: NullReferenceException»Síntoma: Error “Object reference not set to an instance of an object”
Causa: Usar un objeto que es null
Circuito circuito = null;circuito.AgregarComponente(k1); // ❌ ERROR: circuito es null
// Solución: Siempre inicializarCircuito circuito = new Circuito("Principal", 400.0);circuito.AgregarComponente(k1); // ✓ OK⚡ Desafíos (Opcional)
Sección titulada «⚡ Desafíos (Opcional)»Desafío 1: Clase Motor con cálculo de corriente
Sección titulada «Desafío 1: Clase Motor con cálculo de corriente»Dificultad: ⭐☆☆
Crea una clase Motor que herede de ComponenteElectrico y que calcule automáticamente la corriente nominal basada en potencia, voltaje y factor de potencia.
💡 Pista
Usa la fórmula: I = P / (√3 × V × cosφ)
Crea una propiedad FactorPotencia y un método CalcularCorriente().
✅ Solución
public class Motor : ComponenteElectrico{ public double PotenciaKW { get; set; } public double FactorPotencia { get; set; } public int Polos { get; set; } public double Rpm { get; set; }
public Motor(string designacion, double potenciaKW, double voltaje, double factorPotencia, int polos, string fabricante) : base(designacion, "", "Motor", 0, voltaje, fabricante) { this.PotenciaKW = potenciaKW; this.FactorPotencia = factorPotencia; this.Polos = polos;
// Calcular corriente automáticamente this.Corriente = CalcularCorrienteNominal();
// Calcular RPM aproximadas (frecuencia 50Hz) this.Rpm = (120 * 50) / polos; }
private double CalcularCorrienteNominal() { // I = P / (√3 × V × cosφ) return (PotenciaKW * 1000) / (1.732 * Voltaje * FactorPotencia); }
public string ObtenerDatosMotor() { return string.Format("{0}\nPotencia: {1}kW\nRPM: {2}\nFactor de potencia: {3}\nCorriente nominal: {4:F2}A", Designacion, PotenciaKW, Rpm, FactorPotencia, Corriente); }}
// UsoMotor m1 = new Motor("M1", 7.5, 400.0, 0.85, 4, "WEG");MessageBox.Show(m1.ObtenerDatosMotor());Desafío 2: Clase Guardamotor con rango de regulación
Sección titulada «Desafío 2: Clase Guardamotor con rango de regulación»Dificultad: ⭐⭐☆
Crea una clase Guardamotor con propiedades CorrienteMin y CorrienteMax (rango de regulación). Agrega un método que valide si una corriente dada está dentro del rango.
💡 Pista
Usa propiedades CorrienteMin y CorrienteMax, y crea método EsValidaParaMotor(double corrienteMotor).
Desafío 3: Exportar circuito a texto
Sección titulada «Desafío 3: Exportar circuito a texto»Dificultad: ⭐⭐⭐
Agrega un método a la clase Circuito que exporte toda la información del circuito a un archivo de texto (.txt) con formato profesional.
💡 Pista
Usa System.IO.File.WriteAllText() y construye un string con formato tabular.
📝 Resumen
Sección titulada «📝 Resumen»En este capítulo aprendiste:
- ✅ Qué es POO: Programación orientada a objetos como forma de organizar código
- ✅ Clase vs Objeto: Plano (clase) vs instancia concreta (objeto)
- ✅ Propiedades: Almacenar datos del objeto (public/private, calculadas)
- ✅ Constructores: Inicializar objetos correctamente con
new - ✅ Métodos: Definir comportamientos del objeto
- ✅ Encapsulación: Ocultar detalles internos (public/private)
- ✅ Herencia básica: Reutilizar código con
class Hijo : Padre - ✅ Proyecto construido: Sistema completo de gestión de componentes eléctricos
Conceptos clave:
- Las clases son plantillas, los objetos son instancias
newcrea un nuevo objeto a partir de la clase- Las propiedades almacenan datos, los métodos definen comportamientos
- La encapsulación protege datos internos (private) y expone interfaz pública (public)
- POO te prepara para trabajar con EPLAN API (que usa POO intensivamente)
🔗 Conexiones
Sección titulada «🔗 Conexiones»⬅️ Capítulo anterior
Sección titulada «⬅️ Capítulo anterior»Capítulo 9: Colecciones de datos
➡️ Próximo capítulo
Sección titulada «➡️ Próximo capítulo»Capítulo 11: Manejo de errores
En el próximo capítulo aprenderás a manejar errores y excepciones de forma robusta usando try-catch-finally. Construirás validadores que no fallan ante datos incorrectos y aprenderás logging profesional.
📚 Recursos adicionales
Sección titulada «📚 Recursos adicionales»- 📖 Documentación de C# - Clases
- 📖 Documentación de C# - Propiedades
- 📖 Documentación de C# - Constructores
- 📖 Documentación de C# - Encapsulación
- 💻 [Código completo]:
code/cap-10/
❓ Preguntas frecuentes
Sección titulada «❓ Preguntas frecuentes»P: ¿Cuál es la diferencia entre una clase y un objeto?
Sección titulada «P: ¿Cuál es la diferencia entre una clase y un objeto?»R: Una clase es el plano o plantilla (ej. class Contactor). Un objeto es una instancia concreta creada con new (ej. Contactor k1 = new Contactor(...)). Puedes crear muchos objetos a partir de una sola clase.
P: ¿Cuándo debo usar public y cuándo private?
Sección titulada «P: ¿Cuándo debo usar public y cuándo private?»R: Usa public para propiedades y métodos que necesitan ser accesibles desde fuera de la clase. Usa private para datos internos que no deben ser modificados directamente. Regla general: expón solo lo necesario.
P: ¿Puedo tener múltiples constructores en una clase?
Sección titulada «P: ¿Puedo tener múltiples constructores en una clase?»R: Sí, se llama sobrecarga de constructores. Cada constructor debe tener diferente número o tipo de parámetros.
P: ¿Qué es this y cuándo debo usarlo?
Sección titulada «P: ¿Qué es this y cuándo debo usarlo?»R: this se refiere al objeto actual. Úsalo para:
- Distinguir entre parámetros y propiedades con el mismo nombre
- Llamar a otro constructor desde un constructor
- Pasar el objeto actual a otro método
P: ¿Necesito entender POO avanzado para trabajar con EPLAN API?
Sección titulada «P: ¿Necesito entender POO avanzado para trabajar con EPLAN API?»R: No. Con lo aprendido en este capítulo (clases, objetos, propiedades, métodos, constructores) es suficiente para usar EPLAN API. Los conceptos avanzados (interfaces, polimorfismo, clases abstractas) son opcionales.
P: ¿Qué es la herencia y cómo funciona?
Sección titulada «P: ¿Qué es la herencia y cómo funciona?»R: La herencia permite crear una clase nueva (hijo) basada en una clase existente (padre). La clase hijo hereda todas las propiedades y métodos del padre, y puede agregar los suyos propios o sobrescribirlos.
public class ComponenteElectrico { ... } // Padrepublic class Contactor : ComponenteElectrico { ... } // Hijo hereda de padre📋 Checklist de completitud
Sección titulada «📋 Checklist de completitud»Antes de pasar al siguiente capítulo, asegúrate de:
- Entender la diferencia entre clase y objeto
- Poder crear clases con propiedades y métodos
- Saber usar constructores para inicializar objetos
- Entender
publicvsprivate(encapsulación) - Poder instanciar objetos con
new - Comprender qué es la herencia básica
- Haber ejecutado el sistema de gestión de componentes
- Poder agregar nuevas clases (Motor, Guardamotor)
- (Opcional) Haber intentado al menos un desafío
¿Listo para aprender manejo robusto de errores? ¡Vamos al Capítulo 11! 🚀
Última actualización: Enero 2025
Tiempo de lectura estimado: 70-80 minutos
Código de ejemplo: code/cap-10/