COMPARTE ESTE ARTÍCULO

Basado en los ejercicios del PDF proporcionado , este examen presenta cuatro retos de nivel avanzado-moderado. Cada enunciado incluye la solución completa en C#. Está diseñado para que los alumnos trabajen al menos una hora comprendiendo y adaptando cada ejercicio.


1. MascotaVirtual: Serialización y estado

Enunciado:
A partir de la jerarquía de MascotaVirtual, PerroVirtual y GatoVirtual:

  1. Añade al proyecto un método estático GuardarEstado(string ruta, List<MascotaVirtual> mascotas) que serialice la lista de mascotas a JSON (con sus propiedades: nombre, edad, raza y atributos concretos) y la escriba en el archivo indicado.
  2. Implementa List<MascotaVirtual> CargarEstado(string ruta) que lea el JSON y devuelva la lista de objetos correctamente tipados (PerroVirtual o GatoVirtual).
  3. En Main, crea al menos dos perros y dos gatos, guárdalos en "mascotas.json", luego cárgalos de nuevo e imprime por consola los datos de cada mascota.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

// --- Clases de mascota (resumidas) ---
public abstract class MascotaVirtual
{
    public string Nombre { get; }
    public int Edad { get; }
    public string Raza  { get; }
    public MascotaVirtual(string nombre, int edad, string raza)
        => (Nombre, Edad, Raza) = (nombre, edad, raza);
}

public class PerroVirtual : MascotaVirtual
{
    public bool   GanasPasear  { get; }
    public string JuegoFavorito { get; }
    public PerroVirtual(string n, int e, string r, bool gp, string jf)
        : base(n, e, r) => (GanasPasear, JuegoFavorito) = (gp, jf);
}

public class GatoVirtual : MascotaVirtual
{
    public bool TieneArañar { get; }
    public string Humor     { get; }
    public GatoVirtual(string n, int e, string r, bool ta, string h)
        : base(n, e, r) => (TieneArañar, Humor) = (ta, h);
}

// --- Serialización ---
public static class MascotaStorage
{
    static JsonSerializerOptions opts = new JsonSerializerOptions
    {
        WriteIndented = true,
        Converters = { new JsonStringEnumConverter() },
        // Importante: preservar tipo concreto
        TypeInfoResolver = new DomainTypeResolver()
    };

    public static void GuardarEstado(string ruta, List<MascotaVirtual> mascotas)
    {
        string json = JsonSerializer.Serialize(mascotas, opts);
        File.WriteAllText(ruta, json);
    }

    public static List<MascotaVirtual> CargarEstado(string ruta)
    {
        string json = File.ReadAllText(ruta);
        return JsonSerializer.Deserialize<List<MascotaVirtual>>(json, opts);
    }
}

// --- Resolver de tipos para System.Text.Json ---
public class DomainTypeResolver : DefaultJsonTypeInfoResolver
{
    public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
    {
        var ti = base.GetTypeInfo(type, options);
        if (type == typeof(MascotaVirtual))
        {
            ti.PolymorphismOptions = new JsonPolymorphismOptions
            {
                TypeDiscriminatorPropertyName = "$tipo",
                UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization,
                DerivedTypes =
                {
                    new JsonDerivedType(typeof(PerroVirtual), "perro"),
                    new JsonDerivedType(typeof(GatoVirtual), "gato")
                }
            };
        }
        return ti;
    }
}

// --- Programa principal ---
class ProgramaEstado
{
    static void Main()
    {
        var mascotas = new List<MascotaVirtual>
        {
            new PerroVirtual("Roco", 3, "Labrador", true,  "pelota"),
            new PerroVirtual("Luna", 2, "Beagle",    false, "frisbee"),
            new GatoVirtual ("Misu", 1, "Siamés",    true,  "gruñón"),
            new GatoVirtual ("Nube", 4, "Persa",     false, "tranquilo")
        };

        string fichero = "mascotas.json";
        MascotaStorage.GuardarEstado(fichero, mascotas);

        var cargadas = MascotaStorage.CargarEstado(fichero);
        Console.WriteLine("Mascotas cargadas del JSON:");
        foreach (var m in cargadas)
        {
            Console.WriteLine($"{m.GetType().Name}: {m.Nombre}, {m.Edad} años, raza {m.Raza}");
        }
    }
}

2. Estadísticas avanzadas de array

Enunciado:
Tomando como base el array de números del ejercicio original:

  1. Declara y rellena un array con los enteros: 45, 23, 12, 7, 54, 12, 98, 33, 23, 65, 87, 54, 28, 99, 33, 100
  2. Con LINQ, calcula y muestra:
    • Valores únicos (aparecen una sola vez), ordenados ascendentemente.
    • Frecuencia de cada número (Valor → Veces).
    • Mínimo, máximo, promedio y mediana del conjunto.
  3. En Main, imprime cada resultado con claridad.
using System;
using System.Linq;

class EstadisticasArray
{
    static void Main()
    {
        int[] arr = {45,23,12,7,54,12,98,33,23,65,87,54,28,99,33,100};

        // Conteos y únicos
        var grupos = arr
            .GroupBy(x => x)
            .Select(g => new { Valor = g.Key, Veces = g.Count() })
            .OrderBy(x => x.Valor)
            .ToList();

        Console.WriteLine("Valores únicos:");
        foreach (var u in grupos.Where(g => g.Veces == 1))
            Console.WriteLine(u.Valor);

        Console.WriteLine("\nFrecuencia de cada valor:");
        foreach (var g in grupos)
            Console.WriteLine($"Valor: {g.Valor} → Veces: {g.Veces}");

        // Estadísticas
        int    min     = arr.Min();
        int    max     = arr.Max();
        double avg     = arr.Average();
        double median  = CalcularMediana(arr);

        Console.WriteLine($"\nMínimo: {min}");
        Console.WriteLine($"Máximo: {max}");
        Console.WriteLine($"Promedio: {avg:F2}");
        Console.WriteLine($"Mediana: {median:F2}");
    }

    static double CalcularMediana(int[] datos)
    {
        var orden = datos.OrderBy(x => x).ToArray();
        int n = orden.Length;
        if (n % 2 == 1) return orden[n/2];
        return (orden[n/2 - 1] + orden[n/2]) / 2.0;
    }
}

3. CofreSecreto con acceso asíncrono y logging

Enunciado:
Mejora la clase CofreSecreto original para que:

  1. Implemente un método async Task<bool> IntentarAbrirAsync(int[] codigo) que simule cada verificación con un retraso de 500 ms (Task.Delay).
  2. Mantenga un registro en List<string> Log con entradas como "[timestamp] Intento correcto" o "[timestamp] Intento fallido".
  3. Lance CodigoIncorrectoException tras 3 intentos fallidos acumulados (aunque no sean consecutivos).
  4. En Main, itera solicitando códigos hasta abrir el cofre o agotar los intentos. Al finalizar, muestra todo el log.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class CodigoIncorrectoException : Exception
{
    public CodigoIncorrectoException(string msg) : base(msg) { }
}

public class CofreSecreto
{
    private readonly int[] _codigo;
    public List<string> Log { get; } = new();

    private int _fallos = 0;

    public CofreSecreto(params int[] codigo) => _codigo = codigo;

    public async Task<bool> IntentarAbrirAsync(int[] intento)
    {
        await Task.Delay(500);
        bool ok = intento.Length == _codigo.Length
               && !System.Linq.Enumerable.Range(0, _codigo.Length)
                   .Any(i => intento[i] != _codigo[i]);

        string entry = $"[{DateTime.Now:HH:mm:ss}] Intento {(ok ? "correcto" : "fallido")}";
        Log.Add(entry);

        if (!ok && ++_fallos >= 3)
            throw new CodigoIncorrectoException("Número de intentos excedido");

        return ok;
    }
}

class ProgramaCofreAsync
{
    static async Task Main()
    {
        var cofre = new CofreSecreto(4,1,7,9,3);

        while (true)
        {
            Console.Write("Introduce 5 dígitos separados por espacio: ");
            int[] intento = Array.ConvertAll(Console.ReadLine().Split(), int.Parse);
            try
            {
                if (await cofre.IntentarAbrirAsync(intento))
                {
                    Console.WriteLine("¡Cofre abierto!");
                    break;
                }
                else
                {
                    Console.WriteLine("Código incorrecto, prueba de nuevo.");
                }
            }
            catch (CodigoIncorrectoException ex)
            {
                Console.WriteLine(ex.Message);
                break;
            }
        }

        Console.WriteLine("\n=== Log de intentos ===");
        cofre.Log.ForEach(Console.WriteLine);
    }
}

4. Extensión con métodos genéricos y delegados

Enunciado:

  1. Crea un método genérico de extensión PrintIf<T>(this IEnumerable<T> seq, Func<T,bool> predicado) que recorra la secuencia y muestre por consola sólo los elementos que cumplan la condición.
  2. En Main, usa PrintIf sobre:
    • La lista de enteros del ejercicio 2 para mostrar sólo los pares.
    • Una lista de cadenas ("uno","dos","tres","cuatro") para mostrar sólo las de longitud ≥ 4.
using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensiones
{
    public static void PrintIf<T>(
        this IEnumerable<T> seq, Func<T,bool> predicado)
    {
        foreach (var item in seq)
            if (predicado(item))
                Console.WriteLine(item);
    }
}

class ProgramaDelegados
{
    static void Main()
    {
        int[] numeros = {45,23,12,7,54,12,98,33,23,65,87,54,28,99,33,100};
        Console.WriteLine("Números pares:");
        numeros.PrintIf(n => n % 2 == 0);

        var palabras = new List<string>{"uno","dos","tres","cuatro"};
        Console.WriteLine("\nPalabras de longitud ≥ 4:");
        palabras.PrintIf(s => s.Length >= 4);
    }
}

Contenido restringido

Acceso de usuarios existentes
   
Registro de un nuevo usuario
*Campo necesario

Categories:

Tags:

Comments are closed

Estado de acceso
ESTADO DE ACCESO
TRADUCTORES
COMPARTENOS
error: CONTENIDO PROTEGIDO