Artículos de Tecnología

C#: entender clases y structs

André Bessa
André Bessa
capa - Descripción de la imagen: Ilustración con dos personas con las letras VS encima de ellas, representando un combate entre ellas.

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.

¿Qué son las clases?

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.

Pero, ¿qué son las Structs?

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):

¿Usar clases o struct?

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:

Descripción de la imagen: Un bloque pequeño llamado `stack` y Un bloque mayor escrito `heap`.

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:

Descripción de la imagen: Un bloque pequeño llamado “stack” con bloque interno con esta información: edad: int = 28.

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:

Descripción de la imagen: Un bloque pequeño llamado “stack” con bloque interno con esta información: edad: int = 28. Un bloque mayor escrito “heap” con un bloque interno con esta información, respectivamente: miLibro: Libro.

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.

Descripción de la imagen: Un bloque pequeño llamado “stack” con bloques internos con esta información por cada bloque, respectivamente: miLibro*: Libro y Edad: int = 28. Un bloque mayor escrito “heap” con un bloque interno con esta información, respectivamente: miLibro: Libro. Los bloques internos correspondientes del bloque pequeño y del bloque grande se conectan.

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.

Descripción de la imagen: Un bloque pequeño llamado “stack” con bloques internos con esta información por cada bloque, respectivamente: `<<struct>>` persona: Persona, Edad: int = 28, Nombre*: string, Dni*: string y correoElectronico*: string. Un bloque mayor escrito “heap” con bloques internos con estas informaciones, respectivamente: Nombre: string = “André Silva”, Dni: string = “12345678A” y correoElectronico: string = “andre@email.com”. Los bloques internos correspondientes del bloque pequeño y del bloque grande se conectan.

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.

Descripción de la imagen: En la imagen se muestra un fragmento del editor de código Visual Studio Community que indica que el tipo int es una estructura (struct) del tipo readonly.

Otro ejemplo es la struct DateTime, que nos permite trabajar con la fecha y hora del sistema en nuestras aplicaciones. Es bastante interesante, ¿verdad?

Conclusión

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:

foto-autor

André Bessa

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.

Artículos de Tecnología

En Alura encontrarás variados cursos sobre . ¡Comienza ahora!

Precios en:
USD
  • USD
  • BOB
  • CLP
  • COP
  • USD
  • PEN
  • MXN
  • UYU

Semestral

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    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

  • Acceso a todo el contenido de la plataforma por 6 meses
US$ 65.90
un solo pago de US$ 65.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Anual

  • 273 cursos

    Cursos de Programación, Front End, Data Science, Innovación y Gestión.

  • Videos y actividades 100% en Español
  • Certificado de participación
  • Estudia las 24 horas, los 7 días de la semana
  • Foro y comunidad exclusiva para resolver tus dudas
  • Luri, la inteligencia artificial de Alura

    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

  • Acceso a todo el contenido de la plataforma por 12 meses
US$ 99.90
un solo pago de US$ 99.90
¡QUIERO EMPEZAR A ESTUDIAR!

Paga en moneda local en los siguientes países

Acceso a todos
los cursos

Estudia las 24 horas,
dónde y cuándo quieras

Nuevos cursos
cada semana