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
:
- 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. - Implementa
List<MascotaVirtual> CargarEstado(string ruta)
que lea el JSON y devuelva la lista de objetos correctamente tipados (PerroVirtual
oGatoVirtual
). - 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:
- Declara y rellena un array con los enteros:
45, 23, 12, 7, 54, 12, 98, 33, 23, 65, 87, 54, 28, 99, 33, 100
- 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.
- 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:
- Implemente un método
async Task<bool> IntentarAbrirAsync(int[] codigo)
que simule cada verificación con un retraso de 500 ms (Task.Delay
). - Mantenga un registro en
List<string> Log
con entradas como"[timestamp] Intento correcto"
o"[timestamp] Intento fallido"
. - Lance
CodigoIncorrectoException
tras 3 intentos fallidos acumulados (aunque no sean consecutivos). - 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:
- 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. - En
Main
, usaPrintIf
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
Comments are closed