sábado, 12 de febrero de 2022

Curso avanzado de C#: Enumeraciones

Las enumeraciones son "un tipo distinto que consiste en un conjunto de constantes llamado lista de enumeradores ". Es decir, una enumeración es un tipo. Lo que significa que se declarará como un tipo en nuestro código, y otro tipo no podrá tener ese mismo nombre y ser declarado como una enumeración. Cada enumeración debe tener un nombre distinto. 

Curso avanzado de C#: Enumeraciones

Una enumeración es un conjunto, es decir, una agrupación de valores similares. Los valores contenidos en la enumeración reciben nombres, por lo que podemos identificarlos fácilmente, y son constantes, lo que significa que no podemos cambiar sus nombres o sus valores después de crear la enumeración.

Aunque asignemos nombres a los miembros de la lista de enumeración, el compilador en realidad asigna números enteros a los miembros de la lista, y comienzan con 0  incrementándose de uno en uno por cada miembro de la enumeración. Podemos inicializar miembros con nuestro propio valor, lo cual anula este comportamiento por defecto. Por ejemplo utilizaremos los meses del año como constantes nombradas. El siguiente código muestra el uso de una enumeración que contiene los 12 meses del año. La primera muestra utiliza el inicio por defecto. Comienza en 0, mientras que en el segundo ejemplo lo forzamos para que comience con 1. 

// enum llamada Meses, utilizando el inicializador por defecto
 enum Meses
 {
   Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic
 };
 // enum llamada Meses, utilizando una inicialización forzada
 enum Meses2
 {
   Ene = 1, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic
 };

Igual que los nombres de variables, los nombres de los enumeradores no pueden contener espacios. Si queremos utilizar nombres con un espacio, deberemos considerar utilizar NombreConEspacio o Nombre_Con_Espacio. 

En los ejemplos anteriores, la primera muestra utiliza el inicializador predeterminado de 0 y, por lo tanto, los valores en la enumeración contiene los valores de 0 a 11; Ene = 0, Feb = 1, Mar = 2, y así sucesivamente. Pero usualmente utilizamos los meses  como Ene = 1, Feb = 2, Mar = 3 y así sucesivamente. Para mantener esta representación numérica, podemos elegir la segunda muestra del código anterior e iniciar la enumeración
en 1, para adaptarnos a la representación numérica correcta.

El código utiliza un tipo de datos subyacente de int para representar los valores de la lista. Podemos elegir cambiar ese tipo predeterminado, siguiendo al nombre de la enumeración con dos puntos y el tipo de datos como se muestra en el siguiente código.

// utilizando un tipo de datos diferente al de por defecto
  enum Meses3 : byte
  {
   Ene, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic
  };

Sólo están permitidos ciertos tipos de datos para los tipos subyacentes en las  enumeraciones. Los tipos de valor permitidos son:

byte 
sbyte 
short 
ushort 
int 
uint 
long
ulong 

Todos estos tipos son tipos numéricos. Entonces, ¿por qué querríamos elegir un tipo subyacente diferente al predeterminado? Esto depende de nuestros requerimientos para nuestros valores de enumeración. Por ejemplo, si estamos realmente preocupados por la conservación de la memoria en nuestra aplicación, podemos elegir utilizar el tipo byte  como se ha mostrado anteriormente. Un int es un valor de 32 bits, lo que significa 4 bytes mientras que tipo byte que utiliza solo un byte. 

Podemos asignar a cada enumerador su propio valor no secuencial. Por ejemplo, los pilotos de aviones manejan diferentes velocidades de aire para descender con seguridad el tren de aterrizaje y los alerones. Esto se puedes representar por sus designadores de letras y luego asignar las velocidades del aire correspondientes a esos designadores utilizando una enumeración. 

// enumeración para representar las diferentes velocidades del aire
  enum VelAire
  {
      Vx = 55,
      Vy = 65,
      Vs0 = 50,
      Vs1 = 40,
      Vne = 120
  }

En esta enumeración se han establecido cinco enumeradores para representar diferentes velocidades del aire en  un avión. 

Vx es el mejor ángulo de velocidad de ascenso.
Vy es la mejor velocidad de ascenso.
Vs0 es la velocidad de pérdida en una configuración limpia.
Vs1 es la velocidad de pérdida con flaps extendidos.
 Vne es la velocidad que nunca se debe exceder.

Si tuviéramos que escribir código que utilizara solo los valores numéricos, el código sería difícil de leer porque no podríamos descifrar fácilmente el significado de los valores. Con una Enumeración, los programadores entenderían el significado cuando se encuentren en el código los diferentes indicadores de velocidad.

Además de hacer que el código sea más fácil de leer, las enumeraciones tienen otras ventajas, como permitir que otros desarrolladores utilicen nuestra enumeración para saber
claramente cuáles son los valores permitidos para esa enumeración, y alimentar el motor IntelliSense de visual studio. 

Cuando declara una enumeración y luego creamos un nuevo tipo que utiliza nuestra enumeración, IntelliSense muestra los valores permitidos.

Una enumeración es un tipo, es una instancia del tipo System.Enum. System.Enum contiene una serie de métodos que podemos utilizar con nuestras enumeraciones. A continuación hay un código de ejemplo que muestra un par de los métodos disponibles para nosotros cuando trabajamos con enumeraciones. 

IntelliSense Enumeradores C#

Utilizando un enum


class Program
{
        enum Meses { Ene = 1, Feb, Mar, Abr, May, Jun, Jul, Ago, Sep, Oct, Nov, Dic };

        static void Main(string[] args)
        {
            string nombre = Enum.GetName(typeof(Meses), 8); Console.WriteLine("El mes octavo es " + nombre);
            Console.WriteLine("Meses subrayados en la enum:");
            foreach (int values in Enum.GetValues(typeof(Meses)))
            {
                Console.WriteLine(values);
                Console.ReadLine();
            }
    }
}

Análisis

En el ejemplo anterior creamos una enumeración llamada Meses que inicia los valores en 1 e incrementa  el valor predeterminado de 1 para cada mes subsiguiente. En el método main, crea una variable de cadena que llamamos nombre y luego utilizamos un método GetName () de System.Enum para obtener el octavo valor de la enumeración, y lo asignamos a la variable nombre. Como las enumeraciones implementan implícitamente IEnumerable, podemos iterar sobre ellas utilizando foreach. El bucle foreach utiliza el método GetValues () de la clase System.Enum para extraer los valores de la enumeración.

sábado, 5 de febrero de 2022

Curso avanzado de C#: Estructuras de datos

Las estructuras de datos, o simplemente las estructuras, son tipos de valores que se pueden usar para almacenar conjuntos de variables relacionados.
Las estructuras comparten algunas similitudes con las clases, pero también tienen ciertas restricciones. El lenguaje C # proporciona numerosos mecanismos para almacenar datos relacionados, como estructuras, clases, Arrays, colecciones, etc. Cada uno tiene un conjunto específico de requisitos y restricciones que dictan cómo o dónde puedes usarlos.

Curso avanzado de C#: Estructuras de datos

Si consideramos que un objeto de la vida real tiene un conjunto de características, podemos entender cómo modelar este objeto utilizando una estructura. Consideramos por ejemplo un estudiante como un objeto del mundo real que deseamos modelar en código. Podríamos considerar usar una clase para esto, pero si pensamos en las características
que queremos modelar consideramos cómo deseamos utilizar la estructura Estudiante en nuestro código.  Para este ejemplo simple, consideramos la estructura estudiante como un medio para ayudar calcular su calificación promedio. Las características a considerar serían:

Nombre
Apellidos
Clase
Nota 1
Nota 2
Nota 3
Nota 4
Nota 5
Media

Utilizaremos un conjunto de características relativamente simples donde limite el número de  notas a solo 5, proporcionaremos un campo para almacenar la media de todas las pruebas, y campos para el nombre del alumno y su clase. También podríamos haber usado una matriz para las calificaciones, pero de momento crearemos esta estructura en el código: 

public struct Estudiante
{
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
}

La estructura Estudiante creada incluye un conjunto de propiedades representadas por variables de tipo de valor simple, como podemos ver, una estructura es un tipo de valor, pero es un tipo de valor complejo porque puede contener múltiples tipos de valores diferentes como propiedades. 
Para usar esta estructura en el código, necesitamos crear una nueva instancia de ella. No podemos utilizar Estudiante como un nuevo tipo de datos en nuestro código. El siguiente código muestra cómo crear una nueva instancia para la estructura Estudiante: 

// crea una nueva instancia de la estructura estudiante en el código
 Estudiante miEstudiante = new Estudiante();
// crea una nueva instancia de la estructura estudiante sin la instrucción new;
Estudiante miOtroEstudiante;

Después de crear una nueva instancia de la estructura, podemos comenzar a asignar o leer valores de las propiedades declaradas en la estructura. A continuación se muestra la creación de una nueva estructura de tipo Estudiante,  que asigna y lee valores de y hacia las propiedades. Otro pequeño fragmento de código intenta utilizar Estudiante directamente en el código. 

// crea  una nueva instancia de la estructura Estudiante
Estudiante miEstudiante = new Estudiante();
// asigna algunos valores a las propiedades de miEstudiante miEstudiante.nombre = "Pepe";
miEstudiante.nombre = "Juan";
miEstudiante.nota1 = 8;
miEstudiante.nota2 = 9;

Console.Write("Estudiante " + miEstudiante.nombre + " " + miEstudiante.apellido);

Console.Write(" nota " + miEstudiante.nota1 + " en su primer examen. ");
// la instrucción siguiente es incorrecta, no se puede utilizar el tipo directamente
// Visual Studio indicará que se requiere una referencia al objeto  Estudiante.nombre = "Fallo";

Console.ReadLine();  //esta última línea es la más importante pues //detiene la pantalla y nos permite leer el resultado

Las estructuras pueden contener más que solo propiedades. Pueden incluir funciones, constructores, constantes, indexadores, operadores, eventos y tipos anidados y pueden implementar interfaces. Debemos entender El uso de constructores en estructuras porque difieren ligeramente de las clases. Vale la pena señalar los siguientes puntos sobre constructores en estructuras:

Los constructores son opcionales, pero si se incluyen deben contener parámetros. No se permiten constructores por defecto.

Los campos no se pueden inicializar en un cuerpo de estructura.

Los campos se pueden inicializar solo usando el constructor o después de que se haya declarado la estructura. Los miembros privados pueden inicializarse usando solo el constructor.

La creación de un nuevo tipo de estructura sin el nuevo operador no dará lugar a una llamada a un constructor si uno esta presente 

Si nuestra estructura contiene un tipo de referencia (clase) como uno de sus miembros, debemos llamar al el constructor del tipo de referencia explícitamente. 

El siguiente código establece el Nombre del alumno cuando se crea el objeto: 

 // crea una estructura estudiante que utiliza un constructor
 public struct Estudiante
 {
   public string nombre;
    public string apellido;
    private string curso;
    public Estudiante(string nom, string ape, string curso)
    {
      this.nombre = nom;
        this.apellido = ape;
        this.curso = curso;
    }
}

En el código anterior, la estructura se simplifica para mostrar el uso del constructor. Tenemos solo dos campos para el nombre y apellido, y utilizamos el constructor para suministrar esos valores a los campos cuando el objeto se crea con la palabra clave new.

El siguiente fragmento de código de muestra un uso ilegal de un constructor en una estructura. La razón es que si creamos un constructor en una estructura, debemos proporcionar valores para todos los campos de la estructura; si no, se produce un error.

public struct Estudiante
{
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
    public Estudiante(string nom, string ape)
    {
      this.nombre = nom;
        this.apellido = ape;
    }
}

Como se dijo antes, una estructura puede contener funciones también. Podemos crear funciones o métodos en una estructura para poder realizar alguna acción sobre los miembros de datos que contiene. El siguiente fragmento de código muestra un ejemplo  con un método  para calcular la media de la nota, el constructor se ha eliminado para mantener el ejemplo más limpio. 

// crea una estructura estudiante que utiliza un constructor
// Estructura Estudiante  que contiene un método para calcular la nota media
public struct Estudiante
 {
   public string nombre;
    public string apellido;
    public char clase;
    public double nota1;
    public double nota2;
    public double nota3;
    public double nota4;
    public double nota5;
    public double media;
    public void calcMedia()
    {
      double med = ((nota1 + nota2 + nota3 + nota4 + nota5) / 5);
        this.media = med;
    }
}

Desde una perspectiva de código eficiente, habrá que considerar lo más óptimo entre elegir una estructura o una clase.

La estructura Estudiante es simple y debe crearse como estructura para evitar  sobrecargas innecesarias. Es necesario evaluar los escenarios donde deseamos almacenar un número determinado de objetos, ya sea estructura, clase, colección, matriz, etc. teniendo en cuenta que los tipos se pasan por valor, su consumo de memoria puede crecer bastante rápido. En el caso en el que tuviéramos que rellenar bastantes objetos con muchas referencias a estos objetos, en lugar de una estructura de datos del estudiante podríamos considerar una clase.

Creando estructuras (caso práctico)


Abrimos Visual Studio y creamos una aplicación basada en la consola de C # y le ponemos de nombre EstructuraLibros. La estructura del libro contendrá las siguientes propiedades: 

Título
Categoría
Autor
Número de páginas
página actual
ISBN
estilo de la cubierta
Métodos para pasar páginas, llamados PaginaSiguiente y PaginaAnterior. 

Utilizaremos un constructor para  inicializar las propiedades. Un método principal en la aplicación de consola, crearemos la estructura indicada anteriormente para un libro,  y asignaremos sus propiedades al constructor. Utilizando el método console.WriteLine, generaremos cada propiedad en la ventana de la consola y luego llamaremos a los métodos de página siguiente página o anterior. Estos métodos deberán tener en cuenta sólo la página actual y luego aumentar o decrementar  basándose en el método llamado. 

Solución 

public struct Libro
        {
            public string titulo;
            public string categoria;
            public string autor;
            public int numPaginas;
            public int PaginaActual;
            public double ISBN;
            public string cubierta;
            public Libro(string titulo, string categoria, string autor, int numPaginas, int
            PaginaActual, double isbn, string cubierta)
            {
                this.titulo = titulo;
                this.categoria = categoria;
                this.autor = autor;
                this.numPaginas = numPaginas;
                this.PaginaActual = PaginaActual;
                this.ISBN = isbn;
                this.cubierta = cubierta;
            }
            public void PaginaSiguiente()
            {
                if (PaginaActual != numPaginas)
                {
                    PaginaActual++;
                    Console.WriteLine("La página actual es: " + this.PaginaActual);
                }
                else
                {
                    Console.WriteLine("Fin del libro.");
                }
            }
            public void PaginaAnterior()
            {
                if (PaginaActual != 1)
                {
                    PaginaActual--;
                    Console.WriteLine("La página actual es: " + this.PaginaActual);
                }
                else
                {
                    Console.WriteLine("Comienzo del libro.");

                }
            }
        }

        static void Main(string[] args)
        {
            Libro miLibro = new Libro("El Quijote",
            "Novela", "Miguel de Cervantes", 648, 1, 9999999999, "Tapa blanda");
            Console.WriteLine(miLibro.titulo);
            Console.WriteLine(miLibro.categoria);
            Console.WriteLine(miLibro.autor);
            Console.WriteLine(miLibro.numPaginas);
            Console.WriteLine(miLibro.PaginaActual);
            Console.WriteLine(miLibro.ISBN);
            Console.WriteLine(miLibro.cubierta);
            Console.ReadLine();  //esta última línea es la más importante pues
            //detiene la pantalla y nos permite leer el resultado
            miLibro.PaginaSiguiente();
            miLibro.PaginaAnterior();
        }

Los métodos y propiedades se han declarado como públicos. Las estructuras comparten rasgos similares a las clases para la accesibilidad de los miembros de la estructura public  significa que el código tiene acceso a los miembros directamente y puede asignar valores y leer valores, así como llamar a los métodos. 

Si deseamos controlar el acceso a sus miembros debemos declararlos como private en lugar de public. Esto permitirá crear métodos de acceso para las propiedades.
Los métodos de acceso son métodos que son públicos y proporcionan una interfaz a su estructura. Mediante el uso métodos de acceso, podemos configurar los campos miembro de los datos como privados, lo que evitará la escritura o la lectura ellos directamente de modo que los usuarios de nuestra estructura deben pasar por los métodos de acceso.

En estos métodos, podemos incluir un código para verificar la validez de los datos ingresados. Por ejemplo, ¿que pasa si ahora los desarrolladores utilizan esta estructura de código e intentan ingresar un valor de cadena para el número ISBN? Esto daría lugar a un error en el código. En su lugar, nuestro código dentro de la estructura podría realizar la validación en el valor de entrada y devolver un error al código de llamada si el valor no es correcto.