Practica Tema 1 (Gestión de calificaciones de estudiantes) - jcpichardo/Progra-II GitHub Wiki

Requerimientos:

El sistema debe permitir:

  1. Ingresar datos de estudiantes (nombre y calificaciones).
  2. Validar que las calificaciones estén en el rango correcto (0-10).
  3. Limitar el número máximo de estudiantes y materias.
  4. Mostrar un reporte con el promedio de cada estudiante y su estado (aprobado/reprobado).
  5. Proporcionar un menú interactivo para navegar entre opciones.
  6. Garantizar que las entradas del usuario sean seguras y manejadas correctamente.

Flujo del programa:

  1. Muestra un menú interactivo
  2. Permite agregar estudiantes con sus calificaciones
  3. Valida las entradas del usuario
  4. Calcula promedios automáticamente
  5. Muestra reportes con formato
  6. Gestiona errores de entrada

Temas practicar

  1. Comentarios: o Comentarios XML en métodos o Comentarios explicativos en secciones clave

  2. Constantes: o MAX_ESTUDIANTES, MAX_MATERIAS, APROBADO

  3. Tipos de datos: o Primitivos: int, double, bool, string o Complejos: List, Array, struct

  4. Variables: o Uso correcto de ámbitos (locales y de clase) o Nomenclatura descriptiva

  5. Operadores: o Aritméticos: +, -, *, / o Comparación: >, <, ==, >= o Lógicos: &&, || o Asignación: =, +=

  6. Sentencias: o Declaraciones de variables o Asignaciones o Llamadas a métodos

  7. Matrices: o Arreglo unidimensional para calificaciones o Lista genérica para estudiantes

  8. Procedimientos: o MostrarMenu() o AgregarEstudiante()

  9. Funciones: o LeerCadena(), LeerEntero(), LeerDouble() o LeerCalificacion()

  10. Estructuras de control: o if-else o switch o do-while o for o foreach

Creando el proyecto

Abrimos Visual Studio y creamos una aplicación en consola.

image

Programando la aplicación

Borramos cualquier codigo creado y creamos nuestra clase con su namespace.

namespace PracticaIntegradora
{
    class Program
    {
    }
}

Comenzamos con la declaración de constantes y vamos insertando comentarios en el codigo.

 // 1. COMENTARIOS
 // 2. CONSTANTES
 private const int MAX_ESTUDIANTES = 5;
 private const int MAX_MATERIAS = 4;
 private const double APROBADO = 7.0;

Necesitamos donde guardar a los estudiantes, para ello nos funciona una colección de registros llamada estructura..

// 10. ESTRUCTURA (Registro para datos estudiantiles)
struct Estudiante
{
    // 3. TIPOS DE DATOS (string, int, double, bool, List, Array)
    public string Nombre;
    public double[] Calificaciones;
}

Creamos nuestra función de inicio

static void Main(string[] args)
{
}

¿Por que el Main no tiene public o private? ¿Por que es estatico?

Ejemplo para ejecutar el proyecto si Main NO fuera estático

class Program
{
    void Main(string[] args) 
    {
        Console.WriteLine("Hola Mundo");
    }

    static void Ejecutar()
    {
        Program p = new Program(); // Crear instancia
        p.Main(new string[] {});   // Llamar al método
    }
}

Creamos el menú solicitado

        static void MostrarMenu()
        {
            Console.WriteLine("\n\nSistema de Calificaciones");
            Console.WriteLine("1. Agregar estudiante");
            Console.WriteLine("2. Mostrar reporte");
            Console.WriteLine("3. Salir");
        }

Llamamos este método desde la función principal

//9. Funciones y metodos
static void Main(string[] args)
{
    MostrarMenu();
}

Corremos la aplicación

Ahora creamos una función para la lectura del dato de entrada.

static int LeerEntero(string mensaje)
{
    int resultado;
    //Mostramos mensaje de selección
    Console.Write(mensaje);
    //Leemos el dato 
    int.TryParse(Console.ReadLine(), out resultado);
    return resultado;
}

Antes de llamar a la función hagamos uso de los comentarios formato XML, esto nos sirve para tener bien documentados cada uno de nuestros métodos y funciones.

Arriba del comienzo de la función tecleen tres diagonales ///

Aparecerá una estructura similar a esta donde se abrirá un patrón para la documentación del método.

/// <summary>
/// Funcion que devuelve el entero introducido por el usuario
/// </summary>
/// <param name="mensaje">Mensaje que indicara la instrucción al usuario</param>
/// <returns>Retorna un entero que será la selección del usuario</returns>

Llamamos la función desde el método principal, después de mostrar el menú

static void Main(string[] args)
{
    // 4. VARIABLES
    int opcion;

    MostrarMenu();
    opcion = LeerEntero("\nSeleccione una opción:");
}

Creamos una estructura de control para las diferentes opciones del menu

// 10. ESTRUCTURAS DE CONTROL (switch)
switch (opcion)
{
    case 1:
        // 8. PROCEDIMIENTO
        Console.WriteLine("Aqui agregaremos al estudiante");
        break;
    case 2:
        Console.WriteLine("Aqui mostraremos calificaciones");
        break;
    case 3:
        Console.WriteLine("Saliendo del sistema...");
        break;
    default:
        Console.WriteLine("Opción no válida");
        break;
}

Nos damos cuenta que sea cual sea la acción sobre el menu, el sistema termina, aunque no presione la opción 3.

¿Qué hacemos para que no suceda esto?

image
image
image
image
image
¿Que estructura de control podemos usar?

¿Les suena, estructura de control iterativa?

¿Les da una idea un bucle?

¿Como se usa esto, tienen un ejemplo?

  static void Main(string[] args)
  {
      // 4. VARIABLES
      int opcion;

      do
      {
          MostrarMenu();
          opcion = LeerEntero("\nSeleccione una opción:");

          // 10. ESTRUCTURAS DE CONTROL (switch)
          switch (opcion)
          {
              case 1:
                  // 8. PROCEDIMIENTO
                  Console.WriteLine("Aqui agregaremos al estudiante");
                  break;
              case 2:
                  Console.WriteLine("Aqui mostraremos calificaciones");
                  break;
              case 3:
                  Console.WriteLine("Saliendo del sistema...");
                  break;
              default:
                  Console.WriteLine("Opción no válida");
                  break;
          }
      } while (opcion != 3);
  }

Vamos a debuguear para ver que pasa si introducimos letras, asi que pongamos un punto de interrupción en el Switch y en el método LeerEntero.

Ahora sigamos con los requerimientos.

Se solicita dar de alta estudiantes con sus respectivas calificaciones.

Creamos el metodo AgregarEstudiante

static void AgregarEstudiante(ref List<Estudiante> lista)
{
  //Validando el que numero maximo de estudiantes
  if (lista.Count >= MAX_ESTUDIANTES)
  {
      Console.WriteLine("¡Límite de estudiantes alcanzado!");
      return;
  }
  //Si no revaso el limite, crear nuevo estudiante 
  Estudiante nuevo = new Estudiante();
 
}

Para crear al estudiante necesitamos leer el nombre y sus calificaciones.

Vamos a crearlas

Creando la función LeerCadena

 /// <summary>
 /// Funcion para leer una cadena
 /// </summary>
 /// <param name="mensaje">Texti para indicar la instracción al usuario</param>
 /// <returns>Returna una cadena, en caso de valor nulo, retorna una cadena vacia</returns>
 static string LeerCadena(string mensaje)
 {
     string? respuesta ="";
     Console.Write(mensaje);
     //respuesta = respuesta is null ? "" : respuesta; Esta sintaxis la ocuparemos en otro ejemplo
     respuesta = Console.ReadLine() ?? "";
     return respuesta;
 }

Validación de null El operador ?? (null-coalescing operator) es el que maneja la validación de nulos aquí. Si Console.ReadLine() devuelve un null, el operador ?? lo reemplaza por "", evitando que respuesta sea null. Seria lo mismo que tener esto:

respuesta = Console.ReadLine();
if (respuesta == null)
{
    respuesta = "";
}

Creando la funcion LeerCalificacion

 // 9. FUNCIÓN con retorno
 static double LeerCalificacion(string mensaje)
 {
     double calificacion;
     do
     {
         calificacion = LeerDouble(mensaje);
         if (calificacion < 0 || calificacion > 100)
         {
             Console.WriteLine("La calificación debe ser entre 0 y 100");
         }
     } while (calificacion < 0 || calificacion > 100);
     
     return calificacion;
 }

Creamos la funcion LeerDouble

 static double LeerDouble(string mensaje)
 {
     double resultado;
     do
     {
         Console.Write(mensaje);
     } while (!double.TryParse(Console.ReadLine(), out resultado));
     
     return resultado;
 }

Regresamos al método inicial AgregarEstudiante para llamar a la funcion LeerCadena y LeerCalificacion

static void AgregarEstudiante(ref List<Estudiante> lista)
{
  //Validando el que numero maximo de estudiantes
  if (lista.Count >= MAX_ESTUDIANTES)
  {
      Console.WriteLine("¡Límite de estudiantes alcanzado!");
      return;
  }
  //Si no revaso el limite, crear nuevo estudiante 
  Estudiante nuevo = new Estudiante();
 
  nuevo.Nombre = LeerCadena("Nombre del estudiante: ");
  //Validar que el nombre no sea un vacio
  if (nuevo.Nombre== "")
      return;

  // 7. MATRIZ para calificaciones
  nuevo.Calificaciones = new double[MAX_MATERIAS];

  for (int i = 0; i < MAX_MATERIAS; i++)
  {
      nuevo.Calificaciones[i] = LeerCalificacion($"Calificación {i + 1}: ");
  }

  lista.Add(nuevo);
  Console.WriteLine("!Estudiante agregado correctamente!");
}

Ahora regresamos al metodo **Main ** para llamar a la funcion AgregarEstudiante

Pero antes.

image

¿Que necesitamos para guardar los estudiantes?

¿Que creamos al inicio de la práctica?

image
image
image
image

RECORDEMOS

Por lo pronto no tenemos conexión a una base de datos, por lo cual creamos una estructura llamada **Estudiante **que nos guardara la colección de estudiantes con sus calificaciones.

La estructura no es suficiente, ya que esa estructura solo guarda 1 estudiante con un conjunto de calificaciones.

También requerimos una lista del tipo de la estructura de Estudiantes para poder almacenar muchos estudiantes.

Modificar el metodo Main para llamar el guardado del estudiante

 List<Estudiante> estudiantes = new List<Estudiante>();

case 1:
    // 8. PROCEDIMIENTO
    AgregarEstudiante(ref estudiantes);
    break;
image

¿En este punto se entiende por qué la lista estudiantes se pasa como parámetro en una variable por referencia?

Hagamos ahora la funcion de MostrarReporte

Pregunta

image

¿Por que en este caso no pasamos como variable por referencia la lista de estudiantes?

static void MostrarReporte(List<Estudiante> estudiantes)
{
    if (estudiantes.Count == 0)
    {
        Console.WriteLine("No hay estudiantes registrados");
        return;
    }

    Console.WriteLine("\nReporte de Calificaciones");
    Console.WriteLine("=========================");

    // 10. ESTRUCTURAS DE CONTROL (foreach, if-else)
    foreach (var estudiante in estudiantes)
    {
        double promedio = estudiante.Calificaciones.Average();
        bool aprobado = promedio >= APROBADO;

        Console.WriteLine($"\nEstudiante: {estudiante.Nombre}");
        Console.WriteLine($"Promedio: {promedio:N2}");
        Console.WriteLine($"Estado: {(aprobado ? "APROBADO" : "REPROBADO")}");
    }
}

Nota:

Operador ternario (? :): Si respuesta es null, asigna "" (cadena vacía). Si respuesta no es null, deja el mismo valor de respuesta.

Finalmente, hacemos el llamado en el método Main

case 2:
    MostrarReporte(estudiantes);
    break;

Creando la aplicacion de consola desde VSCode

  1. Instala .NET SDK: Asegúrate de tener el .NET SDK instalado en tu sistema. Puedes descargarlo desde aquí.

  2. Crea un nuevo proyecto de consola: Usa el comando dotnet new para crear un nuevo proyecto de consola. dotnet new console -o my-console-app cd my-console-app

  3. Abre el proyecto en Visual Studio Code: code .

  4. Escribe el código: El archivo Program.cs se crea automáticamente.

⚠️ **GitHub.com Fallback** ⚠️