Cuando profundizamos en el aprendizaje de un lenguaje de programación, muchas veces nos encontramos con algunas características muy singulares, propias de un lenguaje en concreto y otras comunes a varios lenguajes, como, por ejemplo, los tipos de estructuras de datos disponibles. En los lenguajes orientados a objetos se utiliza más la estructura sin dudas sobre las clases.
Pero, ¿será siempre la mejor estructura a crear una clase? ¿Cuál es el costo computacional en usar de una clase en comparación con un tipo de valor?
En C#, al igual que en el lenguaje C, hay un tipo de estructura que incluso es muy utilizada por el propio framework .NET, pero no tiene todo el “glamour” de las clases. Hablamos de structs (o estructuras), que pueden ser de gran ayuda para crear códigos que funcionen con pequeños conjuntos de datos.
Pero, ¿cómo creamos una struct? ¿En qué situaciones debo usarlos? Y la clásica pregunta: ¿usar clases o estructuras? En el siguiente texto responderemos algunas de estas preguntas.
Las diversas literaturas técnicas disponibles definen la clase como un molde para crear objetos. Una definición muy interesante es la de Everton Araújo, autor del libro "Orientación a objetos en C#" de la editora “Casa do Código”. Según Araújo, una clase "es un conjunto o grupo de objetos que comparten propiedades y métodos en común".
Pero este concepto de clases y objetos nos lleva al paradigma de programación de la orientación a objetos, que es una forma de pensar y programar software teniendo en cuenta aspectos de nuestra realidad, como un automóvil, una persona o incluso conceptos más abstractos como una cuenta bancaria.
Entonces, con base en estas definiciones, imagina que estás leyendo un libro en particular, para ser más exactos, el libro de Everton Araújo. ¿Este libro es un objeto, verdad? ¿Cuántas páginas tiene este libro? Todo libro tiene algunas características como título, editora, autor, número de páginas, entre otras. Podemos ir más allá y aplicar estas características a otros objetos que se clasifican como libros, ¿verdad? Si es así, podemos crear una clase que contenga estas características. En C#, podemos definir una clase de la siguiente manera:
public class Libro
{
public string Titulo { get; set; }
public string Autor { get; set; }
public int NumeroPaginas { get; set; }
public bool TieneISBN { get; set; }
public string ISBN { get; set; }
public DateTime AnoPublicacion { get; set; }
public string DatosLibro() {
return $"Título: {this.Titulo}"+
$"Autor: {this.Autor}";
}
}
Para utilizar objetos de una clase específica, necesitamos crear objetos de esa clase. Estas estructuras recibirán los valores de las propiedades para representar un "objeto" que existe en el mundo real. Un ejemplo de código:
Libro libro = new Libro()
{
Titulo = "Orientación a objetos en C#",
Autor = "Everton Araújo",
NumeroPaginas = 236,
AnoPublicacao = 2020,
TieneISBN = true,
ISBN = "978-65-86110-00-5"
};
¡Excelente! Pero, ¿existe otra forma de representar una estructura de datos además de una clase? ¡Sí! C# tiene lo que se llama structs
, que en determinadas situaciones se pueden utilizar en lugar de clases.
Las structs, muy comunes en el lenguaje C, tienen como objetivo estructurar algunos datos comunes dentro de un mismo contexto, algo muy similar a la idea de clases. En C#, una struct
es similar a una clase, pero se define como un tipo de valor, mientras que una clase es un tipo de referencia.
Veamos un ejemplo de cómo se define una estructura:
public struct Persona
{
public int Edad { get; set; }
public string Nombre { get; set; }
public string Dni { get; set; }
public string correoElectronico { get; set; }
public override string ToString()
{
return $"Nombre: {this.Nombre}" +
$"Correo Electrónico”: {this.correoElectronico}";
}
}
Una struct
es muy útil para situaciones donde necesitamos pocas variables, ya que los objetos creados a partir de estructuras son más livianos y al ser de tipo valor no funcionan con referencias.
Una struct
puede tener constructores, campos, propiedades, métodos e indexadores. Además, una struct puede implementar una interfaz, aunque no existe herencia entre structs. Para utilizar un objeto creado a partir de una estructura, podemos hacer:
Persona persona = new();
persona.Dni = "12345678A";
persona.Nombre = "André Silva";
persona.correoElectronico = "[email protected]";
persona.Edad = 36;
Es decir, de la misma manera en que creamos una clase.
Resumiendo las características de las estructuras (structs):
Pero, ¿cuándo usar una clase o una struct? Una struct tiene una sintaxis y una definición similares a las de una clase, con algunas limitaciones, pero aún así es fácil de entender e implementar. Para responder si debemos usar clases o structs
, volveremos a hablar sobre tipos de valor (value-type) y por referencia (reference-type).
En general, el compilador trabaja con dos áreas principales en la memoria para la manipulación de datos: la pila (stack
) y el montón (heap
). Observa en la siguiente imagen una abstracción:
El área que define la stack
es mucho más pequeña en comparación con el área del heap
, y ambas funcionan como una abstracción de una pila (el último en entrar es el primero en salir), pero cada uno con un tipo de diferente acceso.
Cuando creamos los tipos de datos más livianos, como los "tipos primitivos" y las estructuras (structs), estos se almacenan directamente en la stack
, ya que son tipos que consumen poco espacio en la memoria. El stack
tiene un rendimiento mucho mejor en la búsqueda de datos en su interior debido a que tiene un espacio menor. Por ejemplo:
En la imagen, nota que la declaración de la variable edad
está junto a su valor, de ahí la definición de tipo de valor (value-type), y el acceso al compilador es muy rápido. Para los tipos más complejos como clases, objetos (objects
), interfaces, delegates y cadenas (strings), estos se almacenan en el heap
, como se muestra en la siguiente imagen:
Sin embargo, el compilador no accede directamente al heap
. ¿Por qué? La respuesta es: ¡porque es un área muy grande! Y en el caso de buscar un objeto del tipo Libro
, podría llevar bastante tiempo optimizando el acceso al heap
, porque el procesador hace uso de la stack
(que es más rápida y pequeña) para realizar esta operación. El compilador crea una referencia en la stack
que apunta a la memoria del heap
donde se encuentra el objeto.
Este espacio en la memoria de la stack
que señala una posición en el heap
es lo que definimos como un puntero, y en la imagen usamos la notación de un asterisco para identificarlo. En resumen, declaramos un puntero en la stack
que guarda una referencia a una posición en el heap
, de ahí la definición de tipo de referencia (reference-type).
Los tipos de referencia se construyen utilizando la palabra reservada new
, que indica que se debe reservar un espacio en la memoria del heap
para un objeto específico.
Recuerda que las structs
son tipos de valor, es decir, se almacenan en la stack
, y aunque esta es más rápida, tiene un espacio mucho menor en comparación con el heap
. Por lo tanto, las structs son recomendadas cuando necesitamos un conjunto de datos más pequeño.
Por ejemplo, en el código presentado para la struct Persona
, si fuera necesario una gran cantidad de propiedades y campos, y la creación de varios objetos del tipo Persona
, sería más interesante crearla como una clase para evitar el desbordamiento de la memoria de la stack
. En la siguiente imagen, se muestra la representación de la struct Persona
en la stack
.
Es importante recordar que la biblioteca de clases .NET también utiliza estructuras (structs) para definir una serie de tipos y funcionalidades. Por ejemplo, tipos como int
, float
o bool
son structs que heredan de una clase base object
.
Otro ejemplo es la struct DateTime
, que nos permite trabajar con la fecha y hora del sistema en nuestras aplicaciones. Es bastante interesante, ¿verdad?
En este artículo, hemos repasado brevemente la definición de clases en programación orientada a objetos, así como los tipos de valor y tipos de referencia, para comprender qué son lasstructs
, cómo definirlas y en qué se diferencian de las clases.
Utilizar structs de manera adecuada en nuestros proyectos puede marcar la diferencia al brindar un mayor rendimiento. Es importante comprender que no toda estructura un poco más compleja necesita ser definida en una clase. ¿Te gustó? ¡Vamos a programar! 😉
Para más información sobre clases y estructuras (structs), consulte:
Soy programador e instructor de programación usando C# y .NET. Licenciado en Sistemas de Información. Ya he programado usando Java, PHP, C#, PostgreSQL y MySQL, además de haber trabajado con soporte también. Siempre buscando aprender más sobre tecnologías, mis aficiones son los cómics y las series.
Este artículo fue traducido para Alura Latam por Brenda Souza.
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Cursos de Programación, Front End, Data Science, Innovación y Gestión.
Luri es nuestra inteligencia artificial que resuelve dudas, da ejemplos prácticos y ayuda a profundizar aún más durante las clases. Puedes conversar con Luri hasta 100 mensajes por semana
Paga en moneda local en los siguientes países
Puedes realizar el pago de tus planes en moneda local en los siguientes países:
País | |||||||
---|---|---|---|---|---|---|---|
Plan Semestral |
487.37
BOB |
69289.36
CLP |
307472.10
COP |
65.90
USD |
264.35
PEN |
1435.53
MXN |
2978.57
UYU |
Plan Anual |
738.82
BOB |
105038.04
CLP |
466107.17
COP |
99.90
USD |
400.74
PEN |
2176.17
MXN |
4515.32
UYU |
Acceso a todos
los cursos
Estudia las 24 horas,
dónde y cuándo quieras
Nuevos cursos
cada semana