Capítulo 10: Clases, objetos y POO en C#
6 min de lectura
Parte I - Sección 2: Programación desde cero - Nivel: Principiante
🎯 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
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
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
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 K1
string k1_designacion = "K1";
string k1_codigo = "3RT2026-1BB40";
double k1_corriente = 25.0;
double k1_voltaje = 400.0;
string k1_fabricante = "Siemens";
// Contactor K2
string 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 Contactor
public 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
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)
↓ instanciar
Objeto k1 → Contactor físico instalado en el tablero
Objeto k2 → Otro contactor físico del mismo tipo
Ejemplo 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 concretas
Contactor k1 = new Contactor(); // Primer contactor
Contactor k2 = new Contactor(); // Segundo contactor
Contactor k3 = new Contactor(); // Tercer contactor
// Cada objeto tiene sus propios valores
k1.Designacion = "K1";
k1.Corriente = 25.0;
k2.Designacion = "K2";
k2.Corriente = 40.0;
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
Las propiedades almacenan los datos de un objeto.
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; }
}
// Uso
Componente comp = new Componente();
comp.Designacion = "K1"; // set
string nombre = comp.Designacion; // get
Explicación:
{ get; set; }crea automáticamente getters y settersget: Permite leer el valorset: Permite escribir el valor
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
}
}
// Uso
Componente comp = new Componente("3RT2026-1BB40");
MessageBox.Show(comp.Codigo); // ✓ OK - lectura permitida
comp.Codigo = "otro"; // ❌ ERROR - escritura no permitida
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);
}
}
}
// Uso
Motor m1 = new Motor();
m1.Potencia = 7.5; // kW
m1.Voltaje = 400.0; // V
m1.FactorPotencia = 0.85; // cosφ
double corriente = m1.CorrienteNominal; // Se calcula automáticamente
MessageBox.Show("Corriente nominal: " + corriente.ToString("F2") + "A");
// "Corriente nominal: 12.77A"
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;
}
// Uso
Guardamotor q1 = new Guardamotor();
MessageBox.Show(q1.Tipo); // "Guardamotor"
MessageBox.Show(q1.Polos.ToString()); // "3"
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)
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
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.Corriente = 25.0;
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)
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 constructores
Contactor k1 = new Contactor("K1", 25.0);
Contactor k2 = new Contactor("K2", "3RT2036-1BB40", 40.0, 400.0);
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
Los métodos son las funciones que definen el comportamiento de un objeto.
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");
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.Activar(); // Muestra mensaje
k1.Desactivar(); // Muestra mensaje
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";
}
}
// Uso
Motor 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
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;
}
}
// Uso
Componente 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
La encapsulación es el principio de ocultar detalles internos y exponer solo lo necesario.
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
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1"; // ✓ OK - public
k1.Activar(); // ✓ OK - public
int contador = k1.ObtenerContadorActivaciones(); // ✓ OK - método public
MessageBox.Show("Activaciones: " + contador);
// k1.estaActivado = true; // ❌ ERROR - private no accesible
// k1.contadorActivaciones++; // ❌ ERROR - private no accesible
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
this se refiere al objeto actual.
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
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
}
}
// Uso
Circuito 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
Vamos a crear un Sistema de Gestión de Componentes Eléctricos completo usando POO.
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)
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)
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
//
// 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
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
Campo (field):
private bool estaActivado; // Almacenamiento directo
Propiedad (property):
public string Designacion { get; set; } // Con getters/setters automáticos
Cuá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 ??
Codigo ?? "N/A"
Significa: "Si Codigo es null, usa "N/A" en su lugar".
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
Error 1: Olvidar new al crear objetos
Síntoma: Error "Use of unassigned variable"
Causa: No instanciar el objeto
// ❌ MAL
Contactor k1;
k1.Designacion = "K1"; // ERROR: k1 no está inicializado
// ✓ BIEN
Contactor k1 = new Contactor("K1", "3RT2026", 25.0, 400.0, 3, "Siemens");
k1.Designacion = "K1"; // OK
Error 2: Confundir clase con objeto
Síntoma: Error "An object reference is required..."
Causa: Intentar llamar métodos de instancia en la clase
// ❌ MAL
Contactor.Activar(); // ERROR: Contactor es la clase, no un objeto
// ✓ BIEN
Contactor k1 = new Contactor(...);
k1.Activar(); // OK: k1 es un objeto
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 inicializar
Circuito circuito = new Circuito("Principal", 400.0);
circuito.AgregarComponente(k1); // ✓ OK
⚡ Desafíos (Opcional)
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);
}
}
// Uso
Motor 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
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
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
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
⬅️ Capítulo anterior
Capítulo 9: Colecciones de datos
➡️ 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
- 📖 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
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?
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?
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?
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?
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?
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 { ... } // Padre
public class Contactor : ComponenteElectrico { ... } // Hijo hereda de padre
📋 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/