viernes, 12 de diciembre de 2025

Lo nuevo de C# 14

 

En está ocasión veremos las nuevas características del lenguaje de programación C#:

  • Miembros de extensión (extension members). 
  • Asignación nula condicional. 
  • Palabra clave field.
  •  nameof para genéricos no enlazados. 
  • Constructores y eventos parciales. 
  • Conversiones implícitas para Span<T>
  • Modificadores en Parámetros Lambda.

1. Miembros de extensión (extension members)

Con los  extension members podemos extender interfaces con miembros adicionales, incluyendo propiedades y métodos, sin modificar la interfaz original. Esto abre la puerta a patrones más limpios, especialmente en librerías y arquitectura modular.

Los extension members permiten:

  • Añadir implementaciones por defecto a interfaces sin romper compatibilidad. 
  • Crear APIs más expresivas sin modificar código fuente de terceros. 
  •  Encapsular comportamientos comunes en un solo lugar. 
  •  Reducir boilerplate en implementaciones repetitivas. 
  •  Evolucionar interfaces sin romper implementaciones existentes.

Ejemplo 1. Extender una interfaz con un método.

Teniendo está interface (que no podemos modificar) y agregar un nuevo método para el cálculo del perímetro.

public interface IShape
{
    double Area();
}

Podemos agregarlo de esta manera sin tocar la interface:

public interface IShape
{
    double Area();
}

public static class ShapeExtensions
{
    public static double Perimeter(this IShape shape)
    {
        return Math.Sqrt(shape.Area() / Math.PI) * 2;

    }
}

Ejemplo 2. Extender una interfaz con una propiedad, lo que antes era imposible.

public interface IShape
{
    double Area();
}

public static class ShapeExtensions
{
    public static double Diameter(this IShape shape)
    {
        return Math.Sqrt(shape.Area() / Math.PI) * 2;
    }

    public static double Radius(this IShape shape)
        => Diameter(shape) / 2;
}

Y ahora:

public static class ShapeExtensions
{
    public static double Radius(this IShape shape)
        => Math.Sqrt(shape.Area() / Math.PI);
}

Podemos usarlo de esta forma:

IShape circle = new Circle(10);
Console.WriteLine(circle.Radius());

2. Asignación nula condicional

Básicamente es una forma de decir: solo asigna este valor si la variable no es null. Su sintaxis es:

x?.atributo = valor;

Ejemplo de uso:

Usuario? usuario = ObtenerUsuario();

usuario?.Edad = 30; // Solo si usuario no es null

En lugar de hacer esto:

if(usuario != null){
  usuario.Edad = 30;
}

Esto nos ayuda con:

  • Menos if repetitivos. 
  • Código más expresivo y seguro. 
  • Menos ruido en handlers, mappers y servicios. 
  • Mejor legibilidad en pipelines donde muchos objetos pueden ser null.


3. Palabra clave field

field es una palabra clave contextual que te permite acceder al campo de respaldo (backing field) que el compilador genera automáticamente para una propiedad. Es decir, te deja usar propiedades automáticas pero con lógica personalizada, sin tener que declarar un campo privado manualmente.

Sirve para:

  • Agregar validaciones en el setter sin crear un campo privado. 
  • Agregar normalización (trim, toUpper, etc.). 
  • Agregar restricciones (rangos, valores mínimos). 
  • Mantener el código limpio y compacto. 
  • Evitar el boilerplate clásico:
private int _edad;
public int Edad
{
    get => _edad;
    set => _edad = value < 0 ? 0 : value;
}

Con field , ya no necesitas ese campo _edad :

public int Edad
{
    get;
    set => field = value < 0 ? 0 : value;
}

field nos ayuda a:

  • No depender de campos extra o campos privados. 
  • Podemos hacer uso de propiedades compactas. 
  • Tener menos código repetitivo. 
  • Tener un código más legible y expresivo.

4. nameof para genéricos no enlazados

Según la documentación oficial: nameof ahora soporta tipos genéricos no enlazados (unbound generic types).

Ejemplo 1. Especificar un tipo concreto.

Antes de C# 14:

nameof(List<int>)  // "List"

Pero no podías hacer esto:

nameof(List<>)   // Error antes de C# 14

Con C# 14 si se pude:

Console.WriteLine(nameof(Dictionary<,>));
// Resultado: "Dictionary"

Console.WriteLine(nameof(List<>));
// Resultado: "List"

5. Constructores y eventos parciales

Con esta nueva carcaterística: puedes declarar constructores como partial, igual que métodos o propiedades.

Esto es útil porque:

  • Puedes dividir un constructor entre varios archivos de una clase parcial. 
  • Una parte puede declarar la firma. 
  • Otra parte puede contener la implementación.

Ejemplo. Veamos un uso de constructor parcial.

Generado automáticamente:

public partial class Persona
{
    public partial Persona(string nombre);
}

Tu implementación:

public partial class Persona
{
    public partial Persona(string nombre)
    {
        Nombre = nombre;
    }

    public string Nombre { get; }
}

Usar esta nueva característica nos sirve para:

  • Separar lógica generada de lógica manual. 
  • Evitar sobrescribir código cuando regeneras archivos. 
  • Mantener clases grandes organizadas. 
  • Permitir que generadores agreguen inicialización sin romper tu constructor.

¿Y qué con los eventos parciales? Pues, ahora puedes declarar eventos como partial.

Esto permite dividir:

  • La declaración del evento. 
  • La implementación del add/remove.

Igual que con los constructores, esto es ideal para escenarios donde parte del código es generado.

¿Para qué sirve?

  • Separar la lógica de suscripción generada de la lógica manual. 
  • Permitir que generadores agreguen eventos sin interferir con tu código. 
  • Mantener clases grandes más limpias.

6. Conversiones implícitas para Span<T>

Con C# 14 se agregan más conversiones implícitas entre Span<T>, ReadOnlySpan<T> y (T[]).

¿Para qué sirven?

Estas conversiones implícitas permiten:

  • Usar Span<T> y ReadOnlySpan<T> de forma más natural. 
  • Evitar copias innecesarias de memoria. 
  •  Escribir código más rápido y eficiente sin complicaciones. 
  •  Simplificar APIs que trabajan con buffers o slices de memoria. 
  •  Reducir la fricción al pasar arreglos a métodos que esperan spans.

En pocas palabras:

Hacen que Span<T> sea un ciudadano de primera clase en el lenguaje.

Ejemplo. Si tenías un método que recibía un Span<int> :

void Procesar(Span<int> datos)
{
    // ...
}

Y querías pasarle un arreglo, necesitabas conversión explícita:

int[] numeros = { 1, 2, 3, 4 };

// Antes: necesitabas conversión explícita
Procesar(numeros.AsSpan());

Ahora con C# 14 lo haces directamente:

int[] numeros = { 1, 2, 3, 4 };

// Conversión implícita
Procesar(numeros);

Resumen:

Conversión Antes de C# 14 Con C# 14 Ejemplo
T[] → Span<T> No implícita Implícita Procesar(numeros);
T[] → ReadOnlySpan<T> No implícita Implícita Leer(numeros);
Span<T> → ReadOnlySpan<T> No implícita Implícita ReadOnlySpan<byte> r = buffer;
string → ReadOnlySpan<char> Ya existía Mejorada Imprimir("Hola");

7. Modificadores en Parámetros Lambda

Esta nueva característica nos permite usar modificadores en los parámetros de una expresión lambda, igual que en un método normal.

Los modificadores permitidos incluyen:

  • ref 
  • in 
  • out 
  • params

Esto significa que puedes escribir lambdas que:

  • Pasan argumentos por referencia. 
  • Evitan copias innecesarias. 
  • Trabajan con parámetros de solo lectura. 
  • Aceptan un número variable de argumentos.

Con esto, las lambdas ahora pueden comportarse como métodos completos.

¿Para qué sirven?

  • Para mejorar el rendimiento (evitar copias grandes). 
  • Para permitir APIs más flexibles. 
  • Para trabajar con estructuras (struct) sin overhead
  • Para escribir código más expresivo y seguro. 
  • Para usar lambdas en escenarios donde antes solo podías usar métodos normales.

Ejemplo 1. Uso de lambda con in (solo lectura). Supongamos que tienes una estructura grande:

public struct Punto
{
    public int X;
    public int Y;
}

Antes, si querías procesarla en una lambda, se copiaba completa. Ahora puedes usar in:

Action<in Punto> imprimir = (in Punto p) =>
{
    Console.WriteLine($"({p.X}, {p.Y})");
};

Uso:

var punto = new Punto { X = 10, Y = 20 };
imprimir(in punto);

Con esto:

  • No se copia la estructura. 
  • Solo de lectura. 
  • Es más eficiente.

Ejemplo 2. Uso de lambda con ref.

var numero = 5;

Action<ref int> duplicar = (ref int n) =>
{
    n *= 2;
};

duplicar(ref numero);

Console.WriteLine(numero); // 10

Con esto: La lambda modifica la variable original. Igual que un método con ref.

Resumen:

Modificador ¿Qué hace? ¿Para qué sirve? Ejemplo
in Pasa el parámetro por referencia de solo lectura Evita copias de estructuras grandes (in Punto p) => Console.WriteLine(p.X);
ref Pasa el parámetro por referencia modificable Permite cambiar el valor original (ref int n) => n *= 2;
out Parámetro de salida Devolver múltiples valores desde una lambda (out int r) => r = 10;
params Acepta un número variable de argumentos Más flexibilidad en lambdas (params int[] v) => v.Sum();

C# es un lenguaje de programación muy completo que puede ser usado en muchos ámbitos. Se puede emplear casi cualquier paradigma de programación existente como la POO y la Programación funcional. Sus nuevas características lo extienden aún más y proveen al desarrollador más herramientas para hacer de su código algo más eficiente y de calidad.

Resumiendo todo lo anteriormente visto tenemos una tabla resumen de lo anteriormente visto:

Característica ¿Qué es? ¿Para qué sirve? Ejemplo breve
Miembros de extensión
(extension members)
Permiten extender interfaces con métodos, propiedades y más. Agregar funcionalidad sin modificar el tipo original. IShape s; s.Perimeter();
Asignación nula condicional Permite asignar solo si el objeto no es null. Evita if (x != null) repetitivos. persona?.Nombre = "Juan";
Palabra clave field Accede al campo de respaldo de una propiedad automática. Permite validaciones sin declarar campos privados. set => field = value.Trim();
nameof para genéricos no enlazados Permite usar nameof con tipos genéricos abiertos. Mejora logging, diagnósticos y metaprogramación. nameof(Dictionary<,>)
Constructores y eventos parciales Permiten dividir su declaración e implementación. Separar código generado del código manual. public partial Persona(...)
Conversiones implícitas para Span<T> Permiten convertir arreglos y spans sin código explícito. Mejor rendimiento y APIs más limpias. Procesar(numeros);
Modificadores en parámetros lambda Permiten usar ref, in, out, params en lambdas. Más control, eficiencia y expresividad. (ref int n) => n++;

Seguiremos con esta serie sobre C#.

Enlaces:

https://learn.microsoft.com/es-es/dotnet/csharp/whats-new/csharp-14


lunes, 8 de diciembre de 2025

Criptomonedas explicadas de manera sencilla (o casi)

En una entrega anterior dimos un vistazo a lo que son las criptomonedas. Las cual es:

  • Una entidad NO FÍSICA  basada en blockchain que solo existe en una red distribuida.
  • Una entidad NO FÍSICA descentralizada, no depende de una entidad gubernamental o bancaria.
  • Una entidad NO FÍSICA segura puesto que utiliza criptografía para realizar sus transacciones, y así evitar copias o fraudes (en teoría). 
  • Una entidad NO FÍSICA cuyo valor es ambiguo o volátil, según la oferta o la demanda (ojo ahí). 
  • Una entidad NO FÍSICA que se almacena en "carteras digitales" llamada Wallets.

Si desglosamos etimológicamente la palabra CRIPTOMONEDA nos encontramos con dos vocablos:

  • Cripto: cubierto, escondido. 
  • Monete o monere: recordar, advertir, aconsejar.

¿Cuál es el sentido, objetivo o fin de las criptomonedas?

Las criptomonedas nacen con un propósito central: crear un sistema de dinero digital que funcione sin depender de bancos, gobiernos o intermediarios, permitiendo transacciones seguras, globales y transparentes.

  • Las criptomonedas buscan ser un sistema de pagos seguro basado en criptografía, sin necesidad de bancos centrales.
  • Su objetivo es ofrecer un medio descentralizado y digital para realizar transacciones financieras, reduciendo la dependencia de instituciones tradicionales. 
  • También responden a la necesidad de finanzas descentralizadas (DeFi) y a la creación de nuevos casos de uso en la economía digital.

Esto se basa en la tecnología blockchain, que garantiza seguridad y descentralización.

Las criptomonedas existen para crear un sistema financiero:

  • Descentralizado.
  • Seguro.
  • Global.
  • Transparente.
  • Accesible para todos. 
  • Innovador y programable.

No son solo dinero digital: son la base de una nueva infraestructura económica.

Las criptomonedas más conocidas son:

  • Bitcoin (BTC).
  • Ethereum (ETH).
  • Litecoin (LTC).

¿Para qué sirve?

  • Para comprar bienes y servicios (aunque no en todos lados). 
  • Para invertir o especular con su precio. 
  • Para transferir dinero de forma rápida y global sin intermediarios.

La tecnología que hace posible a Bitcoin, Ethereum y muchas otras criptomonedas se le conoce como blockchain (cadena de bloques).

La blockchain es, esencialmente, un libro de registros digital distribuido donde se guardan transacciones de forma segura, transparente e inmutable.

1. Estructura: bloques encadenados

Cada bloque contiene:

  • Datos de transacciones.
  • Un hash (identificador único).
  • El hash del bloque anterior.

Esto crea una cadena donde si cambias un bloque, rompes toda la cadena, lo que evita manipulaciones.

2. Red distribuida

Miles de computadoras (nodos) guardan una copia completa de la cadena. Esto elimina la necesidad de un intermediario central y aumenta la seguridad.

3. Mecanismos de consenso

Para validar transacciones, la red usa algoritmos como:

  • Proof of Work (PoW) – usado por Bitcoin.
  • Proof of Stake (PoS) – usado por Ethereum 2.0.

Estos mecanismos permiten que todos los nodos se pongan de acuerdo sin confiar unos en otros.

4. Inmutabilidad y seguridad

Una vez que un bloque se registra, no puede modificarse sin rehacer toda la cadena, lo cual es computacionalmente inviable.

Todo esto suena bonito y agradable, pero ¿Cómo se compran? Veamos un paso a paso.

1. Elige un exchange (plataforma)

Según las guías actuales, en México se usan plataformas como:

  • Binance. 
  • Bitso. 
  • Coinbase. 
  • KuCoin.

Estas plataformas permiten comprar, vender y almacenar criptomonedas.

2. Crea una cuenta y verifica tu identidad

Los exchanges regulados te pedirán:

  • Identificación oficial. 
  • Selfie. 
  • Comprobante básico de datos.

Esto es parte del proceso KYC (Know Your Customer).

3. Deposita fondos

Puedes depositar:

  • Transferencia bancaria SPEI. 
  • Tarjeta de débito. 
  • A veces tarjeta de crédito (con comisiones más altas).

4. Compra la criptomoneda

En la plataforma eliges:

  • La moneda (BTC, ETH, USDT, etc.). 
  • El monto.
  • El tipo de orden (mercado o límite).

5. Guarda tus criptomonedas

Opciones:

  • Wallet del exchange (más fácil, menos segura). 
  • Wallet propia (Metamask, Ledger, Trezor). 
  • Cold wallets para máxima seguridad.

Proceso de transacción de una bitcoin

Veamos un diagrama que describe el flujo de una transacción de bitcoin.

                   ┌──────────────────────────┐
1. Usuario crea transacción│
- Monto                  │
- Dirección destino      │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
2. Wallet selecciona UTXO │
                   │  (entradas disponibles)   │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
3. Wallet construye       │
                   │    la transacción         │
- Inputs (UTXO)          │
- Outputs (destino y     │
                   │    cambio)                │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
4. Firma digital con      │
                   │    clave privada          │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
5. Se envía a un nodo     │
                   │    de la red Bitcoin      │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
6. Nodo valida reglas     │
- Firma válida           │
- UTXO no gastado        │
- Suma entradas ≥ salidas│
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
7. Transacción entra al   │
                   │    MEMPOOL (pendiente)    │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
8. Mineros seleccionan    │
                   │    transacciones según fee│
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
9. Minero arma un bloque  │
                   │    y ejecuta PoW          │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
10. Bloque válido se       │
                   │    difunde a la red       │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
11. Nodos verifican bloque │
                   │    y lo agregan a la      │
                   │    blockchain             │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
12. Transacción CONFIRMADA │
                   │    (1 confirmación)       │
                   └─────────────┬────────────┘


                   ┌──────────────────────────┐
13. Más bloques encima     │
                   │    = más seguridad        │
                   │    (6 confirmaciones)     │
                   └──────────────────────────┘

Este diagrama es solo un ejemplo de transacción.

Conclusión:

El mundo de las criptomonedas es aún lugar recóndito y no muy fácil de navegar (quien diga lo contrario miente). Encontrarás a cada embustero y vende-humos que dirá lo fácil que es usar para comprar e invertir, aunque él no tiene ni idea de cómo hacerlo. Millonarios que heredaron su fortuna que promueven criptomonedas que jamás han usado, cursos sobre mentes millonarias (con un desembolso monetario por supuesto) y cómo lograrlo en sencillos pasos .

A las personas de a pie solo nos queda informarnos y no dejarnos llevar por la ola que arrastra a miles de incautos. No deberíamos creer en todas las maravillas de está tecnología, siempre hay riesgos y casi nadie habla de ello. Siempre hay algo oculto cuando sale algo "innovador".

Como desarrolladores, deberíamos conocer cual es la pauta a seguir. Que plataforma(s) y lenguaje(s) son los que deberíamos aprender.

Enlaces:

https://ethereum.org/en/developers/
https://docs.soliditylang.org/en/latest/
https://codemonkeyjunior.blogspot.com/2025/03/criptomonedas-que-son-y-para-que-sirven.html
https://alquimistadecodigo.blogspot.com/2025/12/comparativa-rholang-vs-plutus-vs.html


viernes, 5 de diciembre de 2025

LINQ: consultando documentos XML

Como vimos en la entrega anterior LINQ nos permite realizar consultas de una base de datos o archivos XML.

Suponiendo que tenemos un documento XML (empleado.xml) con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?>
<Empleados>
<Empleado> 
<Id>4392552</Id>
<Nombre>Horacio</Nombre>
<Apellidos>Gomez Torres</Apellidos>
<Correo>horacio.gomez.tor@infotec.com</Correo>
<Salario>25000.00</Salario>
<Departamento>202</Departamento>
</Empleado>

<Empleado>
<Id>4292856</Id>
<Nombre>Veronica</Nombre>
<Apellidos>Uribe Gomora</Apellidos>
<Correo>veronica.uribe.gom@infotec.com</Correo>
<Salario>30000.00</Salario>
<Departamento>204</Departamento>
</Empleado>


<Empleado>
<Id>4701330</Id>
<Nombre>Karla</Nombre>
<Apellidos>Perez Perez</Apellidos>
<Correo>karla.perez.per@infotec.com</Correo>
<Salario>28000.00</Salario>
<Departamento>200</Departamento>
</Empleado>

</Empleados>

Lo que sigue es la creación de un proyecto .NET que nos permita leer ese documento y hacer consultas:

1. Creamos un nuevo proyecto con dotnet:

$ dotnet new console -n DemoLinqXml
$ cd DemoLinqXml

El documento XML (empleados.xml) debe estar en la misma ruta del proyecto. Ahora abrimos el programa principal Program.cs y lo editamos:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {
        XDocument doc = XDocument.Load("empleados.xml");

        var empleados = doc.Root.Elements("Empleado");

        // Aquí vamos a incluir las consultas
    }
}

Podríamos obtener todos los datos (como un SELECT):

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
                Console.WriteLine("\t Obtener todos los empleados:");
                var todos = empleados.Select(e => new
                {
                    Id = (int)e.Element("Id"),
                    Nombre = (string)e.Element("Nombre"),
                    Apellidos = (string)e.Element("Apellidos"),
                    Correo = (string)e.Element("Correo"),
                    Salario = (decimal)e.Element("Salario"),
                    Departamento = (int)e.Element("Departamento")
                });

                foreach (var emp in todos)
                {
                    Console.WriteLine($"ID: {emp.Id}");
                    Console.WriteLine($"Nombre: {emp.Nombre} {emp.Apellidos}");
                    Console.WriteLine($"Correo: {emp.Correo}");
                    Console.WriteLine($"Salario: {emp.Salario}");
                    Console.WriteLine($"Departamento: {emp.Departamento}");
                    Console.WriteLine("-------------------------");
                }                

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Si queremos obtener solo nombre y correo:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
               
                
                Console.WriteLine("\t Obtener solo nombre y correo:");
                var contactos = empleados
                .Select(e => new
                {
                    Nombre = (string)e.Element("Nombre"),
                    Correo = (string)e.Element("Correo")
                });
                foreach (var contacto in contactos)
                {
                    Console.WriteLine($"{contacto.Nombre} : {contacto.Correo}");
                }

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Obtener el empleado que gana más:

Console.WriteLine("\t Obtener el empleado que gana mas:");
var top = empleados.OrderByDescending(e => (decimal)e.Element("Salario")).First();
Console.WriteLine($"El mayor salario es: \n{top} ");

Obtener todos los empleados que ganen mas de $26000.00 (usando una sintaxis similar a SQL):

Console.WriteLine("\t Obtener todos los empleados que ganen mas de $26000.00:");
var query = from e in empleados
                            where (decimal)e.Element("Salario") > 26000
                            orderby (string)e.Element("Nombre")
                            select new
                            {
                                Nombre = (string)e.Element("Nombre"),
                                Salario = (decimal)e.Element("Salario")
                            };

foreach (var emp in query)
{
  Console.WriteLine($"{emp.Nombre} - {emp.Salario:C}");                
}

Obtener empleado del departamento 202:

Console.WriteLine("\t Obtener empleado del departamento 202:");
var depto202 = empleados.Where(e => (int)e.Element("Departamento") == 202);

foreach (var emp in depto202)
{
   Console.WriteLine($"ID: {(int)emp.Element("Id")}");
   Console.WriteLine($"Nombre: {(string)emp.Element("Nombre")}");
   Console.WriteLine($"Apellidos: {(string)emp.Element("Apellidos")}");
   Console.WriteLine($"Correo: {(string)emp.Element("Correo")}");
   Console.WriteLine($"Salario: {(decimal)emp.Element("Salario")}");
   Console.WriteLine($"Departamento: {(int)emp.Element("Departamento")}");
   Console.WriteLine("-------------------------");
}

Código completo:

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    static void Main()
    {

        try
        {
            XDocument doc = XDocument.Load("empleados.xml");
            var empleados = doc.Root.Elements("Empleado");

            if (empleados != null)
            {
                Console.WriteLine("\t Obtener todos los empleados:");
                var todos = empleados.Select(e => new
                {
                    Id = (int)e.Element("Id"),
                    Nombre = (string)e.Element("Nombre"),
                    Apellidos = (string)e.Element("Apellidos"),
                    Correo = (string)e.Element("Correo"),
                    Salario = (decimal)e.Element("Salario"),
                    Departamento = (int)e.Element("Departamento")
                });

                foreach (var emp in todos)
                {
                    Console.WriteLine($"ID: {emp.Id}");
                    Console.WriteLine($"Nombre: {emp.Nombre} {emp.Apellidos}");
                    Console.WriteLine($"Correo: {emp.Correo}");
                    Console.WriteLine($"Salario: {emp.Salario}");
                    Console.WriteLine($"Departamento: {emp.Departamento}");
                    Console.WriteLine("-------------------------");
                }
                
                Console.WriteLine("\t Obtener solo nombre y correo:");
                var contactos = empleados
                .Select(e => new
                {
                    Nombre = (string)e.Element("Nombre"),
                    Correo = (string)e.Element("Correo")
                });
                foreach (var contacto in contactos)
                {
                    Console.WriteLine($"{contacto.Nombre} : {contacto.Correo}");
                }

                Console.WriteLine("\t Obtener el empleado que gana mas:");
                var top = empleados.OrderByDescending(e => (decimal)e.Element("Salario")).First();
                Console.WriteLine($"El mayor salario es: \n{top} ");

                Console.WriteLine("\t Obtener todos los empleados que ganen mas de $26000.00:");
                var query = from e in empleados
                            where (decimal)e.Element("Salario") > 26000
                            orderby (string)e.Element("Nombre")
                            select new
                            {
                                Nombre = (string)e.Element("Nombre"),
                                Salario = (decimal)e.Element("Salario")
                            };

                foreach (var emp in query)
                {
                    Console.WriteLine($"{emp.Nombre} - {emp.Salario:C}");
                }


                Console.WriteLine("\t Obtener empleado del departamento 202:");
                var depto202 = empleados.Where(e => (int)e.Element("Departamento") == 202);
                foreach (var emp in depto202)
                {
                    Console.WriteLine($"ID: {(int)emp.Element("Id")}");
                    Console.WriteLine($"Nombre: {(string)emp.Element("Nombre")}");
                    Console.WriteLine($"Apellidos: {(string)emp.Element("Apellidos")}");
                    Console.WriteLine($"Correo: {(string)emp.Element("Correo")}");
                    Console.WriteLine($"Salario: {(decimal)emp.Element("Salario")}");
                    Console.WriteLine($"Departamento: {(int)emp.Element("Departamento")}");
                    Console.WriteLine("-------------------------");
                }

            }
        }
        catch (FileNotFoundException ex)
        {
            Console.WriteLine("El archivo no existe: {0}", ex.Message);
        }

    }
}

Ejecutamos el código:

$ dotnet run

Salida:

         Obtener todos los empleados:
ID: 4392552
Nombre: Horacio Gomez Torres
Correo: horacio.gomez.tor@infotec.com
Salario: 25000.00
Departamento: 202
-------------------------
ID: 4292856
Nombre: Veronica Uribe Gomora
Correo: veronica.uribe.gom@infotec.com
Salario: 30000.00
Departamento: 204
-------------------------
ID: 4701330
Nombre: Karla Perez Perez
Correo: karla.perez.per@infotec.com
Salario: 28000.00
Departamento: 200
-------------------------
         Obtener solo nombre y correo:
Horacio : horacio.gomez.tor@infotec.com
Veronica : veronica.uribe.gom@infotec.com
Karla : karla.perez.per@infotec.com
         Obtener el empleado que gana mas:
El mayor salario es:
<Empleado>
  <Id>4292856</Id>
  <Nombre>Veronica</Nombre>
  <Apellidos>Uribe Gomora</Apellidos>
  <Correo>veronica.uribe.gom@infotec.com</Correo>
  <Salario>30000.00</Salario>
  <Departamento>204</Departamento>
</Empleado>
         Obtener todos los empleados que ganen mas de $26000.00:
Karla - $28,000.00
Veronica - $30,000.00
         Obtener empleado del departamento 202:
ID: 4392552
Nombre: Horacio
Apellidos: Gomez Torres
Correo: horacio.gomez.tor@infotec.com
Salario: 25000.00
Departamento: 202
-------------------------

Ventajas y desventajas de usar LINQ for XML

Aquí una pequeña tabla con las ventajas y desventajas del uso de LINQ para leer documentos XML.

Aspecto Ventajas Desventajas
Rendimiento Rápido para XML pequeños o medianos No apto para XML muy grandes (carga completa en memoria)
Legibilidad Sintaxis clara, declarativa y fácil de mantener Puede ser más verboso que XPath en consultas profundas
Tipado Conversión automática a tipos C# Puede lanzar excepciones si faltan nodos o tipos incorrectos
Flexibilidad Fácil de combinar con otras colecciones LINQ Menos eficiente para búsquedas complejas o profundas
Portabilidad Integrado y natural en .NET No es estándar fuera del ecosistema .NET

Seguiremos con esta serie sobre tecnología .NET y C#.

Enlaces:

https://codemonkeyjunior.blogspot.com/2025/11/linq-un-lenguaje-de-consulta-para-c.html
https://learn.microsoft.com/es-es/dotnet/standard/linq/linq-xml-overview

miércoles, 3 de diciembre de 2025

Microsoft Windows Services & Microsoft Worker Services

Windows Services y Worker Services son dos formas de crear procesos de larga duración en el ecosistema de Microsoft.

Windows Services pertenecen al .NET Framework y se ejecutan como servicios del sistema en Windows. Worker Services son la evolución moderna en .NET (Core/5+) y permiten procesos en segundo plano multiplataforma, usando el modelo de hospedaje de .NET.

¿Qué es un Windows Service? es una aplicación de larga duración que se ejecuta en su propia sesión, sin interfaz de usuario, iniciándose con el sistema operativo y administrándose desde el administrador de servicios de Windows. Históricamente se conocen como NT services y están pensados para tareas en segundo plano exclusivamente en Windows

¿Qué es .NET Worker Services? es una aplicación de fondo basada en el host genérico de .NET (IHostedService/BackgroundService). Permite procesar colas, tareas programadas o trabajos intensivos sin UI, y puede ejecutarse en Windows, Linux y macOS. Es la alternativa moderna a los servicios de Windows, con plantillas oficiales y soporte de extensiones de .NET para configuración, DI y logging.

Windows Services

  • Modelo clásico, ligado a Windows. 
  • Pensado para .NET Framework
  • Difícil de portar, instalar y mantener en entornos modernos o cloud.

Worker Services

  • Modelo moderno basado en el Generic Host de .NET
  • Funciona en Windows, Linux y macOS. 
  • Se integra nativamente con DI, logging, configuración, health checks, contenedores y DevOps. 
  • Puede ejecutarse como servicio de Windows, como systemd en Linux o dentro de Docker.

Similitudes:

  • Larga duración: Ambos modelos ejecutan tareas persistentes en segundo plano sin interfaz gráfica. 
  • Automatización de procesos: Son adecuados para trabajos programados, procesamiento de colas y servicios que requieren estar activos continuamente.

Diferencias clave:

  • Plataforma: Windows Services solo en Windows; Worker Services son multiplataforma. 
  • Modelo de hospedaje: Windows Services usan el modelo del SO y ServiceController; Worker Services usan el host genérico de .NET con IHostedService/BackgroundService
  • Desarrollo y despliegue: Windows Services requieren instalación y registro como servicio del sistema; Worker Services se hospedan como consola, servicio de Windows, systemd en Linux o contenedor. 
  • Ecosistema moderno: Worker Services integran DI, configuración y logging de .NET; Windows Services carecen de estas integraciones nativas en .NET Framework.

¿Cuándo usar cada uno?

  • Elige Windows Services: Cuando estás atado a Windows Server y a la administración nativa de servicios del sistema, o mantienes una solución existente en .NET Framework
  • Elige Worker Services: Cuando buscas portabilidad, contenedores, despliegues cloud/devops, integración con DI y observabilidad moderna en .NET.

Comparativa entre Windows Services y Worker Services

He aquí una tabla comparativa:

Aspecto Windows Services (.NET Framework) Worker Services (.NET moderno)
Plataforma Solo Windows Multiplataforma (Windows, Linux, macOS)
Modelo de ejecución Servicio del sistema administrado por SCM (Service Control Manager) Host genérico de .NET (IHostedService/BackgroundService)
Instalación Registro como servicio (sc.exe, instaladores, herramientas de administración) Ejecutable de consola, servicio de Windows, systemd, contenedor
Integraciones Limitadas; sin DI/Logging nativo del host de .NET DI, configuración, logging, health checks, extensiones de .NET
Casos típicos Procesos en background en entornos Windows/legacy Trabajos en background portables, cloud-native, contenedores
Plantillas oficiales Enfoque clásico de .NET Framework Plantilla “Worker Service” en .NET
Mantenimiento Dependiente de Windows y SCM Observabilidad moderna, despliegues flexibles

Worker Services son la evolución moderna de Windows Services.

Creando un Worker Service

Para crear un Worker Service usaremos la herramienta dotnet. Ese servicio se ejecuatrá cada 5 segundos y mostrará un mensaje.

1. Crear el proyecto Worker:

$ dotnet new worker -n MiWorkerService

2. Nos ubicamos en el directorio creado:

$ cd MiWorkerService

3. Editamos la clase Worker.cs

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace MiWorkerService;

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker ejecutándose a las: {time}", DateTimeOffset.Now);
            await Task.Delay(5000, stoppingToken);
        }
    }
}

4. Editamos el programa principal Program.cs

using MiWorkerService;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

5. Ejecutar proyecto:

$ dotnet build
$ dotnet run

Salida:

info: MiWorkerService.Worker[0]          
Worker ejecutándose a las: 12/03/2025 21:10:34 -06:00                        
info: MiWorkerService.Worker[0]                                                          
      Worker ejecutándose a las: 12/03/2025 21:10:39 -06:00                        
info: MiWorkerService.Worker[0]                                                          
      Worker ejecutándose a las: 12/03/2025 21:10:44 -06:00

Podríamos implementarlo como un Servicio, pero será en otra ocasión.

Conclusión: un Worker Service es como un Windows Service modernizado que funciona en cualquier plataforma y listo para ser implementado en la Nube y/o Docker.

Enlaces:

https://learn.microsoft.com/es-es/dotnet/core/extensions/workers
https://medium.com/@adebanjoemmanuel01/running-a-worker-service-as-a-windows-service-c1d12a28a73c
https://web.valuesite.cl/index.php/2024/11/25/la-utilizacion-de-worker-service-para-servicios-automaticos-vs-frameworks/
https://learn.microsoft.com/es-es/dotnet/core/extensions/windows-service

Jai un lenguaje de programación inspirado en C++

Hoy hablaremos de un nuevo lenguaje de programación llamado Jai . Se trata de un lenguaje de programación que está desarrolland...

Etiquetas

Archivo del blog