Mostrando las entradas con la etiqueta .net core. Mostrar todas las entradas
Mostrando las entradas con la etiqueta .net core. Mostrar todas las entradas

domingo, 1 de septiembre de 2024

El comando dotnet parte 2


Antes de empezar lee la parte 1. No te adelantes. 

dotnet new: Crear nuevos proyectos a partir de plantillas

El comando `dotnet new` se utiliza para crear un nuevo proyecto o solución desde una plantilla predeterminada. .NET ofrece una variedad de plantillas que cubren diferentes tipos de aplicaciones, como aplicaciones de consola, aplicaciones web, bibliotecas de clases, y más.

Veamos unos ejemplos: 

dotnet new console -n MyConsoleApp

Este comando crea un nuevo proyecto de consola en C# con el nombre `MyConsoleApp`.

  -n o --name: Especifica el nombre del proyecto o solución.
  -o o --output: Define el directorio donde se creará el proyecto.
  --list: Muestra todas las plantillas disponibles.

Plantillas comunes:
  • console: Aplicación de consola.
  • classlib: Biblioteca de clases.
  • web: Aplicación web ASP.NET Core.
  • mvc: Aplicación ASP.NET Core MVC.
  • blazorserver: Aplicación Blazor Server.

El comando `dotnet build` compila el proyecto y todas sus dependencias, generando los binarios necesarios para la ejecución. Es útil para verificar que el código se puede compilar correctamente y que no hay errores de compilación.

dotnet build

Este comando compila el proyecto en el directorio actual.

-c o --configuration: Especifica la configuración de compilación (por ejemplo, `Debug` o `Release`). El valor predeterminado es `Debug`.
-o o --output: Especifica el directorio de salida para los archivos compilados.

Veamos un ejemplo: 

dotnet build -c Release

dotnet run: Ejecutar un proyecto

El comando `dotnet run` permite compilar y ejecutar una aplicación directamente desde la línea de comandos. Es particularmente útil durante el desarrollo, ya que simplifica el ciclo de construir y ejecutar.
Se escribe: 

dotnet run

Este comando compila y ejecuta el proyecto en el directorio actual.
Podemos agregar: 
--project: Permite especificar un proyecto o solución para ejecutar si estás en un directorio con múltiples proyectos.
 -c o --configuration: Ejecuta el proyecto en la configuración especificada (`Debug`, `Release`, etc.).

Por ejemplo: 

dotnet run --project MyConsoleApp/MyConsoleApp.csproj

dotnet test: Ejecutar pruebas unitarias

El comando `dotnet test` se utiliza para ejecutar pruebas unitarias en un proyecto de pruebas. Este comando ejecuta todas las pruebas definidas en el proyecto y proporciona un informe con los resultados.

Un ejemplo de uso : 

dotnet test

Ejecuta todas las pruebas en el proyecto o solución del directorio actual.
Podemos agregar:
--filter: Filtra las pruebas que se ejecutarán basado en criterios específicos (por ejemplo, nombre del test, categoría).
-l o --logger: Especifica un logger para formatear los resultados de las pruebas.
 --no-build: Evita la compilación del proyecto antes de ejecutar las pruebas, útil si el proyecto ya está compilado.


dotnet test --filter "FullyQualifiedName~MyNamespace.MyTestClass"

dotnet publish: Publicar un proyecto

El comando `dotnet publish` compila el proyecto y empaqueta los archivos necesarios para su despliegue en un entorno específico. Es el paso final antes de implementar la aplicación en producción.

Por ejemplo:

dotnet publish -c Release -o ./publish


Compila el proyecto en modo `Release` y publica los archivos en el directorio `./publish`.

Podemos agregar:
-r o --runtime: Especifica el runtime de destino para la publicación (por ejemplo, `win-x64`, `linux-x64`).
 --self-contained: Genera una publicación que incluye el runtime .NET, lo que permite ejecutar la aplicación en una máquina sin .NET instalado.
-p o --property: Define propiedades adicionales de MSBuild durante la publicación.

Por ejemplo:

dotnet publish -c Release -r win-x64 --self-contained

Estos comandos cubren las operaciones básicas más comunes que los desarrolladores necesitan realizar durante el desarrollo de aplicaciones .NET. Con esta base, podremos gestionar fácilmente proyectos, compilarlos, ejecutarlos, probarlos y publicarlos.

viernes, 16 de agosto de 2024

dotnet el comando con que .net soluciona todos nuestros problemas.


Me he dado cuenta que no conozco en profundidad le comando dotnet, lo uso para correr mis test, para hacer un proyecto de ejemplo pero listo... Pero muchas veces necesitamos abrir esta caja de herramientas y usar todo lo que trae. Por eso me voy a poner a estudiar.. 

Empecemos por el principio. El comando `dotnet` es la herramienta de línea de comandos que viene con el SDK de .NET y que permite a los desarrolladores realizar una amplia gama de tareas relacionadas con la creación, compilación, depuración y despliegue de aplicaciones .NET. Es la interfaz principal para interactuar con el runtime y las bibliotecas de .NET, así como para gestionar paquetes NuGet, herramientas y otros componentes.


dotnet --version


Este comando muestra la versión del SDK de .NET instalado en tu máquina, lo que es útil para verificar rápidamente qué versión estás utilizando.

El comando `dotnet` se utiliza para una variedad de tareas esenciales en el desarrollo de aplicaciones .NET:

  • Crear nuevos proyectos: A través de plantillas, puedes inicializar rápidamente aplicaciones de consola, aplicaciones web, bibliotecas, y más.
  • Compilar código: Facilita la compilación de proyectos .NET en múltiples plataformas.
  • Ejecutar aplicaciones: Puedes ejecutar aplicaciones de consola o servidores web directamente desde la línea de comandos.
  • Gestionar paquetes: Incluye comandos para agregar, actualizar y listar paquetes NuGet en tu proyecto.
  • Probar código: Ejecuta pruebas unitarias para verificar la funcionalidad de tu código.
  • Publicar aplicaciones: Empaqueta y prepara aplicaciones para despliegue en diferentes entornos.


Para crear y ejecutar una simple aplicación de consola, usarías:


dotnet new console -n MyApp

cd MyApp

dotnet run


Este conjunto de comandos crea una nueva aplicación de consola, navega al directorio del proyecto, y ejecuta la aplicación.

Como es de esperar, para usar el comando `dotnet`, necesitas tener instalado el SDK de .NET en tu máquina. El SDK incluye todo lo necesario para desarrollar aplicaciones con .NET, incluyendo el runtime y la herramienta `dotnet`.


Los pasos para instalar el sdk son sencillos: 

Bajar el instalador de https://dotnet.microsoft.com/download. Tenes que elegir la plataforma que usas (Windows, macOS, Linux).

Una vez que tengas el instalador doble click y le das next todas las veces que necesite (sin miedo al exito) 

Luego abris una terminal o línea de comandos y ejecuta:


     dotnet --version


Si el comando devuelve un número de versión, la instalación fue exitosa.


Este SDK es necesario no solo para compilar y ejecutar aplicaciones, sino también para utilizar todas las funcionalidades avanzadas que el comando `dotnet` ofrece.


lunes, 22 de abril de 2024

Usando Roslyn ScriptEngine


La idea es correr C# como un lenguaje script y esto lo podemos hacer con Roslyn. 

Primero, instalamos las dependencias: 


dotnet add package Microsoft.CodeAnalysis.Scripting --version 4.9.2

dotnet add package Microsoft.CodeAnalysis.CSharp.Scripting --version 4.9.2


Y con eso ya estamos, podemos hacer esto: 


using System;

using Microsoft.CodeAnalysis.CSharp.Scripting;

using Microsoft.CodeAnalysis.Scripting;


class Program

{

    static async System.Threading.Tasks.Task Main(string[] args)

    {

        try

        {

            // Creamos el script

            string code = @"

                using System;


                public class MyClass

                {

                    public void MyMethod()

                    {

                        Console.WriteLine(""Hello from C#!"");

                    }

                }";


            // Creamos las opciones (usamos las por defecto) 

            ScriptOptions options = ScriptOptions.Default;


            // Creamos el motor que ejecuta el script

            var script = CSharpScript.Create(code, options);


            // corremos el script

            var result = await script.RunAsync();


            // Y chequeamos si hubo error. 

            if (result.Exception != null)

            {

                Console.WriteLine("Script execution failed: " + result.Exception.Message);

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine("Error executing script: " + ex.Message);

        }

    }

}

Y listo! 

Lo unico malo es que este lenguaje C# no es completo, por ejemplo no se pueden utiliza namespace. Pero para hacer cosas chicas, esta bueno. 

Dejo link: 

lunes, 8 de abril de 2024

ClearScript


ClearScript es una librería que permite ejecutar javascript o VBScript en .NET. Actualmente es compatible con JavaScript (a través de V8 y JScript) y VBScript.

Vamos con un ejemplo:

Primero agregamos la dependencia: 


dotnet add package Microsoft.ClearScript --version 7.4.5


Y bueno, ahora podemos ejecutar javascript por ejemplo: 


using Microsoft.ClearScript;

using Microsoft.ClearScript.V8;


public class Example

{

    static void Main()

    {

        // Create a new V8ScriptEngine

        using var engine = new V8ScriptEngine();

        try

        {

            // Execute JavaScript code

            engine.Execute("var x = 10; var y = 20; var z = x + y;");


            // Get the value of a JavaScript variable

            var zValue = engine.Script.z;


            Console.WriteLine("The value of z is: " + zValue);

        }

        catch (ScriptEngineException ex)

        {

            Console.WriteLine("Error executing JavaScript: " + ex.Message);

        }

    }

}


Y si todo fue bien el resultado es: 


The value of z is: 30


Y listo! 

Dejo link: https://microsoft.github.io/ClearScript/

sábado, 21 de enero de 2023

Pattern matching: las expresiones is y switch, y los operadores and, or y not en C# parte 5


Patron var 

Utiliza un patrón var para hacer coincidir cualquier expresión, incluido nulo, y asigna su resultado a una nueva variable local, como muestra el siguiente ejemplo:


static bool IsAcceptable(int id, int absLimit) =>

    SimulateDataFetch(id) is var results 

    && results.Min() >= -absLimit 

    && results.Max() <= absLimit;


static int[] SimulateDataFetch(int id)

{

    var rand = new Random();

    return Enumerable

               .Range(start: 0, count: 5)

               .Select(s => rand.Next(minValue: -10, maxValue: 11))

               .ToArray();

}


Un patrón var es útil cuando necesitamos una variable temporal dentro de una expresión booleana para contener el resultado de cálculos intermedios. También podemos usar un patrón var cuando necesitamos realizar más comprobaciones cuando las mayúsculas y minúsculas protegen una expresión o declaración de cambio, como muestra el siguiente ejemplo:

public record Point(int X, int Y);

static Point Transform(Point point) => point switch

{

    var (x, y) when x < y => new Point(-x, y),

    var (x, y) when x > y => new Point(x, -y),

    var (x, y) => new Point(x, y),

};


static void TestTransform()

{

    Console.WriteLine(Transform(new Point(1, 2)));  // output: Point { X = -1, Y = 2 }

    Console.WriteLine(Transform(new Point(5, 2)));  // output: Point { X = 5, Y = -2 }

}


En el ejemplo anterior, el patrón var (x, y) es equivalente a un patrón posicional (var x, var y).

En un patrón var, el tipo de una variable declarada es el tipo de tiempo de compilación de la expresión que se compara con el patrón.


Patrón descartar 

Utiliza un patrón de descarte _ para hacer coincidir cualquier expresión, incluido nulo, como muestra el siguiente ejemplo:

Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday));  // output: 5.0

Console.WriteLine(GetDiscountInPercent(null));  // output: 0.0

Console.WriteLine(GetDiscountInPercent((DayOfWeek)10));  // output: 0.0


static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch

{

    DayOfWeek.Monday => 0.5m,

    DayOfWeek.Tuesday => 12.5m,

    DayOfWeek.Wednesday => 7.5m,

    DayOfWeek.Thursday => 12.5m,

    DayOfWeek.Friday => 5.0m,

    DayOfWeek.Saturday => 2.5m,

    DayOfWeek.Sunday => 2.0m,

    _ => 0.0m,

};


En el ejemplo anterior, se usa un patrón de descarte para controlar valores nulos y enteros que no tienen el miembro correspondiente de la enumeración DayOfWeek. Eso garantiza que una expresión de cambio en el ejemplo maneje todos los valores de entrada posibles. Si no usa un patrón de descarte en una expresión de cambio y ninguno de los patrones de la expresión coincide con una entrada, el tiempo de ejecución genera una excepción. El compilador genera una advertencia si una expresión de cambio no maneja todos los valores de entrada posibles.

Un patrón de descarte no puede ser un patrón en una expresión is o una declaración de cambio. En esos casos, para hacer coincidir cualquier expresión, podemos usar un patrón var con un descarte: var _.


Patrón entre paréntesis

A partir de C# 9.0, podemos poner paréntesis alrededor de cualquier patrón. Por lo general, hace eso para enfatizar o cambiar la precedencia en patrones lógicos, como muestra el siguiente ejemplo:

if (input is not (float or double))

{

    return;

}

Patrones de lista

A partir de C# 11, puede hacer coincidir una matriz o una lista con una secuencia de patrones, como muestra el siguiente ejemplo:

int[] numbers = { 1, 2, 3 };


Console.WriteLine(numbers is [1, 2, 3]);  // True

Console.WriteLine(numbers is [1, 2, 4]);  // False

Console.WriteLine(numbers is [1, 2, 3, 4]);  // False

Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

Como muestra el ejemplo anterior, un patrón de lista coincide cuando cada patrón anidado coincide con el elemento correspondiente de una secuencia de entrada. Puede usar cualquier patrón dentro de un patrón de lista. Para hacer coincidir cualquier elemento, use el patrón de descarte o, si también desea capturar el elemento, el patrón var, como muestra el siguiente ejemplo:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])

{

    Console.WriteLine($"The first element of a three-item list is {first}.");

}

// Output:

// The first element of a three-item list is 1.

Los ejemplos anteriores comparan una secuencia de entrada completa con un patrón de lista. Para hacer coincidir los elementos solo al principio o al final de una secuencia de entrada, podemos usar el patrón de división .. dentro de un patrón de lista, como muestra el siguiente ejemplo:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True

Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True

Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False

Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False


Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True

Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False

Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True


Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True

Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True

Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False


Un patrón de división coincide con cero o más elementos. Puede usar como máximo un patrón de división en un patrón de lista.

También puede anidar un subpatrón dentro de un patrón de división, como muestra el siguiente ejemplo:

void MatchMessage(string message)

{

    var result = message is ['a' or 'A', .. var s, 'a' or 'A']

        ? $"Message {message} matches; inner part is {s}."

        : $"Message {message} doesn't match.";

    Console.WriteLine(result);

}


MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.

MatchMessage("apron");  // output: Message apron doesn't match.


void Validate(int[] numbers)

{

    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";

    Console.WriteLine(result);

}


Validate(new[] { -1, 0, 1 });  // output: not valid

Validate(new[] { -1, 0, 0, 1 });  // output: valid


miércoles, 18 de enero de 2023

Pattern matching: las expresiones is y switch, y los operadores and, or y not en C# parte 4


Patrón posicional

Utiliza un patrón posicional para deconstruir un resultado de expresión y hacer coincidir los valores resultantes con los patrones anidados correspondientes, como muestra el siguiente ejemplo:

public readonly struct Point

{

    public int X { get; }

    public int Y { get; }


    public Point(int x, int y) => (X, Y) = (x, y);


    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);

}


static string Classify(Point point) => point switch

{

    (0, 0) => "Origin",

    (1, 0) => "positive X basis end",

    (0, 1) => "positive Y basis end",

    _ => "Just a point",

};

En el ejemplo anterior, el tipo de una expresión contiene el método Deconstruct, que se utiliza para deconstruir el resultado de una expresión. También puede hacer coincidir expresiones de tipos de tupla con patrones posicionales. De esa manera, puede hacer coincidir varias entradas con varios patrones, como muestra el siguiente ejemplo:

static decimal GetGroupTicketPriceDiscount(int groupSize, DateTime visitDate)

    => (groupSize, visitDate.DayOfWeek) switch

    {

        (<= 0, _) => throw new ArgumentException("Group size must be positive."),

        (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => 0.0m,

        (>= 5 and < 10, DayOfWeek.Monday) => 20.0m,

        (>= 10, DayOfWeek.Monday) => 30.0m,

        (>= 5 and < 10, _) => 12.0m,

        (>= 10, _) => 15.0m,

        _ => 0.0m,

    };

El ejemplo anterior usa patrones relacionales y lógicos, que están disponibles en C# 9.0 y versiones posteriores.

Se puede usar los nombres de los elementos de tupla y los parámetros de Deconstrucción en un patrón posicional, como muestra el siguiente ejemplo:

var numbers = new List<int> { 1, 2, 3 };

if (SumAndCount(numbers) is (Sum: var sum, Count: > 0))

{

    Console.WriteLine($"Sum of [{string.Join(" ", numbers)}] is {sum}");  // output: Sum of [1 2 3] is 6

}


static (double Sum, int Count) SumAndCount(IEnumerable<int> numbers)

{

    int sum = 0;

    int count = 0;

    foreach (int number in numbers)

    {

        sum += number;

        count++;

    }

    return (sum, count);

}


También puede extender un patrón posicional de cualquiera de las siguientes maneras:

  • Agregue una verificación de tipo en tiempo de ejecución y una declaración de variable, como muestra el siguiente ejemplo:

public record Point2D(int X, int Y);

public record Point3D(int X, int Y, int Z);


static string PrintIfAllCoordinatesArePositive(object point) => point switch

{

    Point2D (> 0, > 0) p => p.ToString(),

    Point3D (> 0, > 0, > 0) p => p.ToString(),

    _ => string.Empty,

};

El ejemplo anterior usa registros posicionales que proporcionan implícitamente el método Deconstruct.

  • Se puede utilizar un patrón de propiedad dentro de un patrón posicional, como muestra el siguiente ejemplo:


public record WeightedPoint(int X, int Y)

{

    public double Weight { get; set; }

}


static bool IsInDomain(WeightedPoint point) => point is (>= 0, >= 0) { Weight: >= 0.0 };


  • Y Tambien se puede combinar los dos usos anteriores, como muestra el siguiente ejemplo:


if (input is WeightedPoint (> 0, > 0) { Weight: > 0.0 } p)

{

    // ..

}


Un patrón posicional es un patrón recursivo. Es decir, puede utilizar cualquier patrón como patrón anidado.


lunes, 16 de enero de 2023

Pattern matching: las expresiones is y switch, y los operadores and, or y not en C# parte 3


Patrones lógicos

A partir de C# 9.0, utiliza los combinadores de patrones not, and y or para crear los siguientes patrones lógicos:

  • Negación que coincide con una expresión cuando el patrón negado no coincide con la expresión. El siguiente ejemplo muestra cómo puede negar un patrón nulo constante para verificar si una expresión no es nula:

if (input is not null)

{

    // ...

}

 

  • Conjuntiva (and) que coincide con una expresión cuando ambos patrones coinciden con la expresión. El siguiente ejemplo muestra cómo puede combinar patrones relacionales para verificar si un valor está en un cierto rango:

Console.WriteLine(Classify(13));  // output: High
Console.WriteLine(Classify(-100));  // output: Too low
Console.WriteLine(Classify(5.7));  // output: Acceptable

static string Classify(double measurement) => measurement switch
{
    < -40.0 => "Too low",
    >= -40.0 and < 0 => "Low",
    >= 0 and < 10.0 => "Acceptable",
    >= 10.0 and < 20.0 => "High",
    >= 20.0 => "Too high",
    double.NaN => "Unknown",
};

  • Disyuntiva (or) que coincide con una expresión cuando cualquiera de los patrones coincide con la expresión, como muestra el siguiente ejemplo:

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19)));  // output: winter

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9)));  // output: autumn

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11)));  // output: spring


static string GetCalendarSeason(DateTime date) => date.Month switch

{

    3 or 4 or 5 => "spring",

    6 or 7 or 8 => "summer",

    9 or 10 or 11 => "autumn",

    12 or 1 or 2 => "winter",

    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),

};

Como muestra el ejemplo anterior, puede utilizar repetidamente los combinadores de patrones en un patrón.


Precedencia y orden de verificación

La siguiente lista ordena los combinadores de patrones comenzando desde la precedencia más alta hasta la más baja:

  • not
  • and
  • or

Para especificar explícitamente la precedencia, podemos usar paréntesis, como muestra el siguiente ejemplo:

static bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');


Patrón de propiedad

Se utiliza un patrón de propiedad para hacer coincidir las propiedades o campos de una expresión con patrones anidados, como muestra el siguiente ejemplo:

static bool IsConferenceDay(DateTime date) => date is { Year: 2020, Month: 5, Day: 19 or 20 or 21 };

Un patrón de propiedad coincide con una expresión cuando el resultado de una expresión no es nulo y cada patrón anidado coincide con la propiedad o el campo correspondiente del resultado de la expresión.

También puede agregar una verificación de tipo en tiempo de ejecución y una declaración de variable a un patrón de propiedad, como muestra el siguiente ejemplo:


Console.WriteLine(TakeFive("Hello, world!"));  // output: Hello

Console.WriteLine(TakeFive("Hi!"));  // output: Hi!

Console.WriteLine(TakeFive(new[] { '1', '2', '3', '4', '5', '6', '7' }));  // output: 12345

Console.WriteLine(TakeFive(new[] { 'a', 'b', 'c' }));  // output: abc


static string TakeFive(object input) => input switch

{

    string { Length: >= 5 } s => s.Substring(0, 5),

    string s => s,


    ICollection<char> { Count: >= 5 } symbols => new string(symbols.Take(5).ToArray()),

    ICollection<char> symbols => new string(symbols.ToArray()),


    null => throw new ArgumentNullException(nameof(input)),

    _ => throw new ArgumentException("Not supported input type."),

};

Un patrón de propiedad es un patrón recursivo. Es decir, puede utilizar cualquier patrón como patrón anidado. Se puede utilizar un patrón de propiedad para hacer coincidir partes de datos con patrones anidados, como muestra el siguiente ejemplo:

public record Point(int X, int Y);

public record Segment(Point Start, Point End);


static bool IsAnyEndOnXAxis(Segment segment) =>

    segment is { Start: { Y: 0 } } or { End: { Y: 0 } };


El ejemplo anterior usa dos funciones disponibles en C# 9.0 y versiones posteriores: o combinador de patrones y tipos de registro.

A partir de C# 10, puede hacer referencia a propiedades o campos anidados dentro de un patrón de propiedad. Esta capacidad se conoce como patrón de propiedad extendida. Por ejemplo, puede refactorizar el método del ejemplo anterior en el siguiente código equivalente:


static bool IsAnyEndOnXAxis(Segment segment) =>

    segment is { Start.Y: 0 } or { End.Y: 0 };



viernes, 13 de enero de 2023

Pattern matching: las expresiones is y switch, y los operadores and, or y not en C# parte 2


Patrón constante

Utiliza un patrón constante para probar si el resultado de una expresión es igual a una constante especificada, como muestra el siguiente ejemplo:


public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch

{

    1 => 12.0m,

    2 => 20.0m,

    3 => 27.0m,

    4 => 32.0m,

    0 => 0.0m,

    _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),

};


En un patrón constante, se puede usar cualquier expresión constante, como:

  • un literal numérico entero o de coma flotante
  • un char
  • un literal de cadena.
  • un valor booleano verdadero o falso
  • un valor de enumeración
  • el nombre de un campo const declarado o local
  • nulo

La expresión debe ser un tipo que se pueda convertir al tipo constante, con una excepción: una expresión cuyo tipo sea Span<char> o ReadOnlySpan<char> se puede comparar con cadenas constantes en C# 11 y versiones posteriores.

Se puede utilizar un patrón constante para verificar si hay valores nulos, como muestra el siguiente ejemplo:


if (input is null)

{

    return;

}


El compilador garantiza que no se invoca ningún operador de igualdad sobrecargado por el usuario == cuando se evalúa la expresión x es nula.

A partir de C# 9.0, puede usar un patrón de constante nulo negado para verificar si no es nulo, como muestra el siguiente ejemplo:


if (input is not null)

{

    // ...

}


Patrones relacionales

A partir de C# 9.0, utiliza un patrón relacional para comparar el resultado de una expresión con una constante, como muestra el siguiente ejemplo:


Console.WriteLine(Classify(13));  // output: Too high

Console.WriteLine(Classify(double.NaN));  // output: Unknown

Console.WriteLine(Classify(2.4));  // output: Acceptable


static string Classify(double measurement) => measurement switch

{

    < -4.0 => "Too low",

    > 10.0 => "Too high",

    double.NaN => "Unknown",

    _ => "Acceptable",

};

En un patrón relacional, puede utilizar cualquiera de los operadores relacionales <, >, <= o >=. La parte derecha de un patrón relacional debe ser una expresión constante. La expresión constante puede ser de tipo entero, punto flotante, carácter o enumeración.

Para verificar si el resultado de una expresión está en un cierto rango, compárelo con una conjuntiva y un patrón, como muestra el siguiente ejemplo:


Console.WriteLine(GetCalendarSeason(new DateTime(2021, 3, 14)));  // output: spring

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 7, 19)));  // output: summer

Console.WriteLine(GetCalendarSeason(new DateTime(2021, 2, 17)));  // output: winter


static string GetCalendarSeason(DateTime date) => date.Month switch

{

    >= 3 and < 6 => "spring",

    >= 6 and < 9 => "summer",

    >= 9 and < 12 => "autumn",

    12 or (>= 1 and < 3) => "winter",

    _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),

};


Si el resultado de una expresión es nulo o no se convierte al tipo de una constante mediante una conversión anulable o unboxing, un patrón relacional no coincide con una expresión.



miércoles, 11 de enero de 2023

Pattern matching: las expresiones is y switch, y los operadores and, or y not en C#


Las siguientes expresiones e instrucciones de C# admiten pattern matching:

  • la expresión is 
  • la sentencia switch 
  • y la expresión switch 

En esas construcciones, se puede hacer coincidir una expresión de entrada con cualquiera de los siguientes patrones:

  • Patrón de declaración: para verificar el tipo de tiempo de ejecución de una expresión y, si una coincidencia tiene éxito, asignar un resultado de expresión a una variable declarada.
  • Patrón de tipo: para comprobar el tipo de tiempo de ejecución de una expresión. 
  • Patrón constante: para probar si el resultado de una expresión es igual a una constante especificada.
  • Patrones relacionales: para comparar el resultado de una expresión con una constante especificada. 
  • Patrones lógicos: para probar si una expresión coincide con una combinación lógica de patrones. 
  • Patrón de propiedad: para probar si las propiedades o campos de una expresión coinciden con patrones anidados.
  • Patrón posicional: para deconstruir un resultado de expresión y probar si los valores resultantes coinciden con patrones anidados.
  • patrón var: para hacer coincidir cualquier expresión y asignar su resultado a una variable declarada.
  • Patrón de descarte: para que coincida con cualquier expresión.
  • Patrones de lista: para probar si los elementos de secuencia coinciden con los patrones anidados correspondientes. 
Los patrones lógicos, de propiedad, posicionales y de lista son patrones recursivos. Es decir, pueden contener patrones anidados.

Patrones de tipo y declaración

Se utiliza patrones de declaración y tipo para verificar si el tipo de tiempo de ejecución de una expresión es compatible con un tipo dado. Con un patrón de declaración, también se puede declarar una nueva variable local. Cuando un patrón de declaración coincide con una expresión, a esa variable se le asigna un resultado de expresión convertida, como muestra el siguiente ejemplo:

object greeting = "Hello, World!";
if (greeting is string message)
{
    Console.WriteLine(message.ToLower());  // output: hello, world!
}

Un patrón de declaración con tipo T coincide con una expresión cuando el resultado de una expresión no es nulo y cualquiera de las siguientes condiciones es verdadera:

  • El tipo de tiempo de ejecución de un resultado de expresión es T.
  • El tipo de tiempo de ejecución de un resultado de expresión se deriva del tipo T, implementa la interfaz T o existe otra conversión de referencia implícita a T. El siguiente ejemplo muestra dos casos en los que esta condición sean verdadera:

var numbers = new int[] { 10, 20, 30 };
Console.WriteLine(GetSourceLabel(numbers));  // output: 1

var letters = new List<char> { 'a', 'b', 'c', 'd' };
Console.WriteLine(GetSourceLabel(letters));  // output: 2

static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
{
    Array array => 1,
    ICollection<T> collection => 2,
    _ => 3,
};

En el ejemplo anterior, en la primera llamada al método GetSourceLabel, el primer patrón coincide con un valor de argumento porque el tipo de tiempo de ejecución del argumento int[] se deriva del tipo Array. En la segunda llamada al método GetSourceLabel, el tipo List<T> en tiempo de ejecución del argumento no se deriva del tipo Array pero implementa la interfaz ICollection<T>.

  • El tipo de tiempo de ejecución de un resultado de expresión es un tipo de valor que acepta valores NULL con el tipo T subyacente.
  • Existe una conversión boxing o unboxing del tipo de tiempo de ejecución de un resultado de expresión al tipo T.
El siguiente ejemplo demuestra las dos últimas condiciones:

int? xNullable = 7;
int y = 23;
object yBoxed = y;
if (xNullable is int a && yBoxed is int b)
{
    Console.WriteLine(a + b);  // output: 30
}

Si desea verificar solo el tipo de una expresión, puede usar un descarte _ en lugar del nombre de una variable, como muestra el siguiente ejemplo:

public abstract class Vehicle {}
public class Car : Vehicle {}
public class Truck : Vehicle {}

public static class TollCalculator
{
    public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
    {
        Car _ => 2.00m,
        Truck _ => 7.50m,
        null => throw new ArgumentNullException(nameof(vehicle)),
        _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
    };
}

A partir de C# 9.0, para ello puede utilizar un patrón de tipo, como muestra el siguiente ejemplo:

public static decimal CalculateToll(this Vehicle vehicle) => vehicle switch
{
    Car => 2.00m,
    Truck => 7.50m,
    null => throw new ArgumentNullException(nameof(vehicle)),
    _ => throw new ArgumentException("Unknown type of a vehicle", nameof(vehicle)),
};


Al igual que un patrón de declaración, un patrón de tipo coincide con una expresión cuando el resultado de una expresión no es nulo y su tipo de tiempo de ejecución satisface cualquiera de las condiciones enumeradas anteriormente.

Para verificar si no es nulo, puede usar un patrón constante nulo negado, como muestra el siguiente ejemplo:

if (input is not null)
{
    // ...
}

Bueno, hasta acá con la declaración, en post posteriores seguire con los demas puntos. 


viernes, 2 de diciembre de 2022

Cuando utilizar estructuras, registro o clases en C#?


Que sé yo... Un lenguaje cuantas más cosas trae, a mi entender más complijidad contrae y más dificil es aclarar ciertos puntos... 

Y para colmo se pueden hacer más cosas de diferentes formas, haciendo que se pierda la claridad del mejor camino para resolver las cosas.  Puff ... Y sin duda C#, java, scala, kotlin, etc... son muy completos y por ende muy complejos de entender cuando usar que. 

Luego de esta introducción/opinión, vamos al tema del post. Cuando utilizar estructuras, registro o clases en C#?

¿Puede el tipo de datos ser un tipo de valor? entonces es un estructura. ¿No? ¿Su tipo describe un estado similar a un valor, preferiblemente inmutable? entonces registro. Y sino clase pero podemos usar registro para:  

  • DTO si el flujo unidireccional.
  • Immutable request
  • SearchParameters

Veamos esto con más detalle. Una estructura, una clase y un registro son tipos de datos de usuario.

Las estructuras son tipos de valor. Las clases son tipos de referencia. Los registros son por defecto tipos de referencia inmutables.

Cuando necesita algún tipo de jerarquía para describir sus tipos de datos como herencia o una estructura que apunta a otra estructura o básicamente cosas que apuntan a otras cosas, necesita un tipo de referencia.

Los registros resuelven el problema cuando desea que su tipo esté orientado a valores de forma predeterminada. Los registros son tipos de referencia pero con la semántica orientada al valor.

En conclusión, 

Estructura si: 

  • Representa lógicamente un solo valor, similar a los tipos primitivos (int, double, etc.).
  • Tiene un tamaño de instancia inferior a 16 bytes.
  • Es inmutable
  • No tendrá que ser referenciado con frecuencia.

¿No? Debe ser algún tipo de referencia (clase o registro).

¿El tipo de datos encapsula algún tipo de valor complejo? ¿El valor es inmutable? ¿Lo usa en flujo unidireccional (una vía)? Vamos con registro.

¿No? Vamos con clase.

Por cierto: no te olvides que habrá registros anónimos en C# 10.0, como para ponerle más complejidad.

Otra cosa, una instancia de registro puede ser mutable si la convierte en mutable.


class Program

{

    static void Main()

    {

        var test = new Foo("a");

        Console.WriteLine(test.MutableProperty);

        test.MutableProperty = 15;

        Console.WriteLine(test.MutableProperty);

        //test.Bar = "new string"; // will not compile

    }

}


public record Foo(string Bar)

{

    public double MutableProperty { get; set; } = 10.0;

}


Una asignación de un registro es una copia superficial del registro. Una copia con expresión de un registro no es ni superficial ni profunda. La copia se crea mediante un método de clonación especial emitido por el compilador de C#. Los miembros de tipo de valor se copian y encuadran. Los miembros de tipo de referencia apuntan a la misma referencia. Puede hacer una copia profunda de un registro si y solo si el registro solo tiene propiedades de tipo de valor. Cualquier propiedad de miembro de tipo de referencia de un registro se copia como una copia superficial.

Veamos este ejemplo (usando la función de nivel superior en C# 9.0):


using System.Collections.Generic;

using static System.Console;


var foo = new SomeRecord(new List<string>());

var fooAsShallowCopy = foo;

var fooAsWithCopy = foo with { }; // A syntactic sugar for new SomeRecord(foo.List);

var fooWithDifferentList = foo with { List = new List<string>() { "a", "b" } };

var differentFooWithSameList = new SomeRecord(foo.List); // This is the same like foo with { };

foo.List.Add("a");


WriteLine($"Count in foo: {foo.List.Count}"); // 1

WriteLine($"Count in fooAsShallowCopy: {fooAsShallowCopy.List.Count}"); // 1

WriteLine($"Count in fooWithDifferentList: {fooWithDifferentList.List.Count}"); // 2

WriteLine($"Count in differentFooWithSameList: {differentFooWithSameList.List.Count}"); // 1

WriteLine($"Count in fooAsWithCopy: {fooAsWithCopy.List.Count}"); // 1

WriteLine("");


WriteLine($"Equals (foo & fooAsShallowCopy): {Equals(foo, fooAsShallowCopy)}"); // True. The lists inside are the same.

WriteLine($"Equals (foo & fooWithDifferentList): {Equals(foo, fooWithDifferentList)}"); // False. The lists are different

WriteLine($"Equals (foo & differentFooWithSameList): {Equals(foo, differentFooWithSameList)}"); // True. The list are the same.

WriteLine($"Equals (foo & fooAsWithCopy): {Equals(foo, fooAsWithCopy)}"); // True. The list are the same, see below.

WriteLine($"ReferenceEquals (foo.List & fooAsShallowCopy.List): {ReferenceEquals(foo.List, fooAsShallowCopy.List)}"); // True. The records property points to the same reference.

WriteLine($"ReferenceEquals (foo.List & fooWithDifferentList.List): {ReferenceEquals(foo.List, fooWithDifferentList.List)}"); // False. The list are different instances.

WriteLine($"ReferenceEquals (foo.List & differentFooWithSameList.List): {ReferenceEquals(foo.List, differentFooWithSameList.List)}"); // True. The records property points to the same reference.

WriteLine($"ReferenceEquals (foo.List & fooAsWithCopy.List): {ReferenceEquals(foo.List, fooAsWithCopy.List)}"); // True. The records property points to the same reference.

WriteLine("");


WriteLine($"ReferenceEquals (foo & fooAsShallowCopy): {ReferenceEquals(foo, fooAsShallowCopy)}"); // True. !!! fooAsCopy is pure shallow copy of foo. !!!

WriteLine($"ReferenceEquals (foo & fooWithDifferentList): {ReferenceEquals(foo, fooWithDifferentList)}"); // False. These records are two different reference variables.

WriteLine($"ReferenceEquals (foo & differentFooWithSameList): {ReferenceEquals(foo, differentFooWithSameList)}"); // False. These records are two different reference variables and reference type property hold by these records does not matter in ReferenceEqual.

WriteLine($"ReferenceEquals (foo & fooAsWithCopy): {ReferenceEquals(foo, fooAsWithCopy)}"); // False. The same story as differentFooWithSameList.

WriteLine("");


var bar = new RecordOnlyWithValueNonMutableProperty(0);

var barAsShallowCopy = bar;

var differentBarDifferentProperty = bar with { NonMutableProperty = 1 };

var barAsWithCopy = bar with { };


WriteLine($"Equals (bar & barAsShallowCopy): {Equals(bar, barAsShallowCopy)}"); // True.

WriteLine($"Equals (bar & differentBarDifferentProperty): {Equals(bar, differentBarDifferentProperty)}"); // False. Remember, the value equality is used.

WriteLine($"Equals (bar & barAsWithCopy): {Equals(bar, barAsWithCopy)}"); // True. Remember, the value equality is used.

WriteLine($"ReferenceEquals (bar & barAsShallowCopy): {ReferenceEquals(bar, barAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (bar & differentBarDifferentProperty): {ReferenceEquals(bar, differentBarDifferentProperty)}"); // False. Operator with creates a new reference variable.

WriteLine($"ReferenceEquals (bar & barAsWithCopy): {ReferenceEquals(bar, barAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


var fooBar = new RecordOnlyWithValueMutableProperty();

var fooBarAsShallowCopy = fooBar; // A shallow copy, the reference to bar is assigned to barAsCopy

var fooBarAsWithCopy = fooBar with { }; // A deep copy by coincidence because fooBar has only one value property which is copied into barAsDeepCopy.


WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used.

WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


fooBar.MutableProperty = 2;

fooBarAsShallowCopy.MutableProperty = 3;

fooBarAsWithCopy.MutableProperty = 3;

WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 3

WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used. 3 != 4

WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


fooBarAsWithCopy.MutableProperty = 4;

WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 4

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // False. Remember, the value equality is used. 3 != 4

WriteLine("");


var venom = new MixedRecord(new List<string>(), 0); // Reference/Value property, mutable non-mutable.

var eddieBrock = venom;

var carnage = venom with { };

venom.List.Add("I'm a predator.");

carnage.List.Add("All I ever wanted in this world is a carnage.");

WriteLine($"Count in venom: {venom.List.Count}"); // 2

WriteLine($"Count in eddieBrock: {eddieBrock.List.Count}"); // 2

WriteLine($"Count in carnage: {carnage.List.Count}"); // 2

WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True.

WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // True. Value properties has the same values, the List property points to the same reference.

WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.

WriteLine("");


eddieBrock.MutableList = new List<string>();

eddieBrock.MutableProperty = 3;

WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True. Reference or value type does not matter. Still a shallow copy of venom, still true.

WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // False. the venom.List property does not points to the same reference like in carnage.List anymore.

WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.

WriteLine($"ReferenceEquals (venom.List & carnage.List): {ReferenceEquals(venom.List, carnage.List)}"); // True. Non mutable reference type.

WriteLine($"ReferenceEquals (venom.MutableList & carnage.MutableList): {ReferenceEquals(venom.MutableList, carnage.MutableList)}"); // False. This is why Equals(venom, carnage) returns false.

WriteLine("");



public record SomeRecord(List<string> List);


public record RecordOnlyWithValueNonMutableProperty(int NonMutableProperty);


public record RecordOnlyWithValueMutableProperty

{

    public int MutableProperty { get; set; } = 1; // this property gets boxed

}


public record MixedRecord(List<string> List, int NonMutableProperty)

{

    public List<string> MutableList { get; set; } = new();

    public int MutableProperty { get; set; } = 1; // this property gets boxed

}


La penalización de rendimiento es obvia aquí. A mayor cantidad de datos para copiar en una instancia de registro que tenga, mayor penalización de rendimiento obtendrá. En general, debe crear clases pequeñas y livianas y esta regla también se aplica a los registros.

Si su aplicación usa una base de datos o un sistema de archivos, no me preocuparía mucho por esta penalización. Las operaciones de la base de datos/sistema de archivos son generalmente más lentas.


sábado, 19 de noviembre de 2022

C# 11 y .NET 7 traen análisis genérico


Uno de los patrones de diseño básicos en .NET es la función Parse estática. Prácticamente todas las clases que se pueden instanciar a partir de una cadena tienen una. Sin embargo, antes de C# 11 y .NET 7, no había forma de crear una interfaz que se adaptara a esto.

El problema fundamental es que antes de ahora, las interfaces abstractas (es decir, la palabra clave de la interfaz) no admitían funciones estáticas. Esto no era solo una limitación de C#, el CLR en sí no lo admitía antes de la introducción de la característica de métodos abstractos estáticos en las interfaces. Al igual que con los métodos de interfaz, los métodos abstractos estáticos pueden tener una implementación predeterminada, pero existen limitaciones.

Veamos interfaz IParsable<TSelf> ofrece dos métodos, Parse y TryParse. Ambos aceptan un String y un IFormatProvider. Para acceder a ellos, necesita un método de ayuda genérico como:


static T Parse<T>(string s, IFormatProvider? provider)

    where T : IParsable<T>

{

    return T.Parse(s, provider);

}


Se puede invocarlo usando un parámetro de tipo.


var a = Parse<int>("4", null);



martes, 15 de noviembre de 2022

Roslyn, compiladores de C# y Visual Basic con unas APIs para crear herramientas de análisis de código.

Roslyn es la implementación de código abierto de los compiladores de C# y Visual Basic con unas APIs para crear herramientas de análisis de código.

La idea es que el compilador exponga información del proceso de compilación para crear herramientas que permitan mejorar ese código. 

Los compiladores procesan el código siguiendo reglas estructuradas que a menudo difieren de la forma en que los humanos leen y entienden el código. Una comprensión básica del modelo utilizado por los compiladores es esencial para comprender las API que utiliza al crear herramientas basadas en Roslyn.

El SDK de .NET Compiler Platform expone el análisis de código de los compiladores de C# y Visual Basic, al proporcionar una capa de API que refleja una canalización de compilador tradicional.

Cada fase de este proceso es un componente separado. Primero, la fase de análisis tokeniza y analiza el texto fuente en sintaxis que sigue la gramática del lenguaje. En segundo lugar, la fase de declaración analiza la fuente y los metadatos importados para formar símbolos con nombre. A continuación, la fase de enlace hace coincidir los identificadores en el código con los símbolos. Finalmente, la fase de emisión emite un ensamblado con toda la información construida por el compilador.

En correspondencia con cada una de esas fases, el SDK de .NET Compiler Platform expone un modelo de objeto que permite el acceso a la información en esa fase. La fase de análisis expone un árbol de sintaxis, la fase de declaración expone una tabla de símbolos jerárquicos, la fase de vinculación expone el resultado del análisis semántico del compilador y la fase de emisión es una API que produce códigos de bytes IL.


Cada compilador combina estos componentes como un único todo de extremo a extremo.

Estas API son las mismas que usa Visual Studio. Por ejemplo, las funciones de esquematización y formato del código usan los árboles de sintaxis, el Examinador de objetos y las funciones de navegación usan la tabla de símbolos, las refactorizaciones e Ir a definición usan el modelo semántico, y Edit and Continue usa todo esto, incluida la API Emit.

La capa del compilador contiene los modelos de objetos que corresponden a la información expuesta en cada fase de la canalización del compilador, tanto sintáctica como semántica. La capa del compilador también contiene una instantánea inmutable de una única invocación de un compilador, incluidas las referencias de ensamblado, las opciones del compilador y los archivos de código fuente. Hay dos API distintas que representan el lenguaje C# y el lenguaje Visual Basic. Las dos API tienen una forma similar pero están diseñadas para ofrecer alta fidelidad a cada lenguaje individual. Esta capa no tiene dependencias en los componentes de Visual Studio.

Como parte de su análisis, el compilador puede producir un conjunto de diagnósticos que cubren todo, desde errores de sintaxis, semánticos y de asignación definitiva hasta varias advertencias y diagnósticos informativos. La capa de API del compilador expone diagnósticos a través de una API extensible que permite que los analizadores se conecten al proceso de compilación. Permite que los diagnósticos definidos por el usuario, como los producidos por herramientas como StyleCop, se produzcan junto con los diagnósticos definidos por el compilador. Producir diagnósticos de esta manera tiene la ventaja de integrarse naturalmente con herramientas como MSBuild y Visual Studio, que dependen de los diagnósticos para experiencias como detener una compilación basada en una política y mostrar IL en vivo en el editor y sugerir correcciones de código.

Las API de alojamiento y secuencias de comandos forman parte de la capa del compilador. Puede usarlos para ejecutar fragmentos de código y acumular un contexto de ejecución en tiempo de ejecución. El REPL interactivo de C# (Read-Evaluate-Print Loop) utiliza estas API. El REPL le permite utilizar C# como lenguaje de secuencias de comandos, ejecutando el código de forma interactiva a medida que lo escribe.

La capa Workspaces contiene la API Workspace, que es el punto de partida para realizar análisis de código y refactorizar soluciones completas. Lo ayuda a organizar toda la información sobre los proyectos en una solución en un solo modelo de objeto, ofreciéndole acceso directo a los modelos de objetos de la capa del compilador sin necesidad de analizar archivos, configurar opciones o administrar dependencias de proyecto a proyecto.

Además, la capa Workspaces muestra un conjunto de API que se utilizan al implementar herramientas de análisis y refactorización de código que funcionan dentro de un entorno de host como el IDE de Visual Studio. Los ejemplos incluyen las API Buscar todas las referencias, Formato y Generación de código. Esta capa no tiene dependencias en los componentes de Visual Studio.

viernes, 11 de noviembre de 2022

Microsoft lanza .NET 7



Y porque es tan importante esto?  .NET 7 es una plataforma de desarrollo multiplataforma unificada. 

La idea era unificar .NET Core (reescrito desde cero, open source y multiplataforma) con la tecnología de .NET Framework (más versátil, pero limitada a sistemas Windows), así como con productos como Xamarin y Mono, para crear un producto que permitiera programar sobre una única base de código con runtimes y experiencias de desarrollo uniformes.

Sin embargo, en agosto de 2020, Microsoft anunció que estos pasos se pospondrían hasta el lanzamiento de .NET 6, previsto para noviembre de 2021. Tras eso, la compañía anunció "cambios en el cronograma" que obligaron a postergar de nuevo la integración de componentes como MAUI (Multi-platform App UI). Y así hasta hoy.

Una de las grandes novedades de .NET 7 es la inclusión de la versión 11 de la sintaxis de C#, el lenguaje de programación estrella de Microsoft (F#, también ha sido actualizado).

Además, el anuncio de Microsoft incluye referencias a mejoras de rendimiento, especialmente en ARM64 (con un rendimiento hasta un 45% superior) y acuerdos de colaboración para desarrollar con .NET en Ubuntu Linux, y en los servidores Power System de IBM.

Dejo link : https://news.microsoft.com/es-xl/net-7-esta-disponible-hoy/

martes, 6 de julio de 2021

Diferencia entre Null y String vació en C#


Una cadena vacía es una instancia de un objeto System.String que contiene cero caracteres. Las cadenas vacías se utilizan a menudo en varios escenarios de programación para representar un campo de texto en blanco. Puede llamar a métodos en cadenas vacías porque son objetos System.String válidos. Las cadenas vacías se inicializan de la siguiente manera: string s = String.Empty;

Por el contrario, una cadena nula no se refiere a una instancia de un objeto System.String y cualquier intento de llamar a un método en una cadena nula provoca una NullReferenceException. Sin embargo, puede utilizar cadenas nulas en operaciones de concatenación y comparación con otras cadenas. Los siguientes ejemplos ilustran algunos casos en los que una referencia a una cadena nula hace y no provoca que se lance una excepción:


Las operaciones de cadenas en .NET están altamente optimizadas y, en la mayoría de los casos, no afectan significativamente el rendimiento. Sin embargo, en algunos escenarios, como bucles  que se ejecutan cientos o miles de veces, las operaciones de cadenas pueden afectar el rendimiento. La clase StringBuilder crea un búfer de cadenas que ofrece un mejor rendimiento para estos casos. La cadena StringBuilder también le permite reasignar caracteres individuales, algo que el tipo de datos de cadena incorporado no admite. 

Web API en F# y .net 5

Vamos a hacer una web Api con F# y Entity Framework. 

Primero hacer la web Api e instalar Entity Framework. 

dotnet new webapi --language "F#"

dotnet add package Swashbuckle.AspNetCore --version 5.6.3

dotnet new tool-manifest

dotnet tool install dotnet-ef

dotnet tool install paket

dotnet paket convert-from-nuget

dotnet paket add Microsoft.EntityFrameworkCore.Sqlite

dotnet paket add EntityFrameworkCore.FSharp

code  .


Ahora creamos el modelo: 

Hice un archivo Prueba.fs : 

namespace webAPIFsharp

open System

[<CLIMutable>]
type Prueba =
{ Id: int
Name : string
Summary: string }

Y hacemos el dbcontext : 

namespace webAPIFsharp

open System.ComponentModel.DataAnnotations
open Microsoft.EntityFrameworkCore
open EntityFrameworkCore.FSharp.Extensions

type ApiDbContext() =
inherit DbContext()

[<DefaultValue>]
val mutable pruebas : DbSet<Prueba>

member public this.Pruebas with get() = this.pruebas
and set p = this.pruebas <- p

override _.OnModelCreating builder =
builder.RegisterOptionTypes() // enables option values for all entities

override __.OnConfiguring(options: DbContextOptionsBuilder) : unit =
options.UseSqlite("Data Source=base.db") |> ignore

Ahora debemos indicar que compile estas clases agregando estas entradas en el fsproj : 

<Compile Include="Prueba.fs" />
<Compile Include="ApiDbContext.fs" />

Luego compilamos 

dotnet build

Si funciona debemos hacer la migración : 

dotnet ef migrations add Initial

Y ahora agregamos la migración a fsproj :

<Compile Include="Migrations/*.fs" />

Y ahora creamos la base : 

dotnet ef database update

Y ahora agregamos el context a el Startup.fs : 

// Configure EF
services.AddDbContext<ApiDbContext>() |> ignore

Y luego hacemos el controler : 

namespace webAPIFsharp.Controllers

open System
open System.Collections.Generic
open System.Linq
open System.Threading.Tasks
open Microsoft.EntityFrameworkCore
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.Logging
open webAPIFsharp

[<ApiController>]
[<Route("[controller]")>]
type PruebaController (logger : ILogger<PruebaController>,
dbContext : ApiDbContext) =
inherit ControllerBase()


[<HttpGet>]
member _.Get() = ActionResult<IEnumerable<Prueba>>(dbContext.Pruebas)

[<HttpPost>]
member _.PostSync(prueba:Prueba) =
dbContext.Pruebas.Add prueba |> ignore
dbContext.SavedChanges |> ignore


[<HttpPost("async")>]
member _.PostAsync(entity: Prueba) =
async {
dbContext.Pruebas.AddAsync(entity) |> ignore
let! _ = dbContext.SaveChangesAsync true |> Async.AwaitTask
return entity
}

[<HttpGet("{id}")>]
member _.getById id =
dbContext.Pruebas
|> Seq.tryFind (fun f -> f.Id = id)

Y listo! 

dotnet run

Yo agregue swagger para mayor detalles dejo link de github: 

https://github.com/emanuelpeg/FsharpWebApiExample


sábado, 3 de julio de 2021

Clases y métodos genéricos en C#


Generics introduce el concepto de parámetros de tipo en .NET, lo que hace posible diseñar clases y métodos que difieren la especificación de uno o más tipos hasta que la clase o método sea declarado e instanciado por el código del cliente. Veamos un ejemplo: 


Las clases y métodos genéricos combinan la reutilización, la seguridad de tipos y la eficiencia de una manera que sus contrapartes no genéricas no pueden. Los genéricos se utilizan con mayor frecuencia con las colecciones y los métodos que operan en ellas. El espacio de nombres System.Collections.Generic contiene varias clases de colección basadas en genéricos. Las colecciones no genéricas, como ArrayList, no se recomiendan y se mantienen por motivos de compatibilidad.

Veamos un ejemplo: 



Un método declarado con los parámetros de tipo para su tipo o parámetros de retorno se denomina método genérico.



Los métodos AddorUpdate () y GetData () son métodos genéricos. El tipo de datos real del parámetro del elemento se especificará en el momento de instanciar la clase DataStore <T>, como se muestra a continuación.


Una clase no genérica puede incluir métodos genéricos especificando un parámetro de tipo entre paréntesis angulares con el nombre del método, como se muestra a continuación.


viernes, 2 de julio de 2021

DateTime en C# parte 2


 Seguimos con: https://emanuelpeg.blogspot.com/2021/07/datetime-en-c.html

La estructura DateTime incluye los siguientes métodos para convertir una fecha y hora en una cadena.

Método

Descripción

ToString

Convierte un valor DateTime en una cadena en el formato especificado de la referencia cultural actual. Permite pasar el formato de fecha por parámetros.

ToShortDateString

formato M/d/yyyy

ToShortTimeString

formato h:mm:ss

ToLongDateString

formato dddd, MMMM d, yyyy

ToLongTimeString

formato h:mm:ss tt




Veamos un ejemplo: 



Una cadena de fecha y hora válida se puede convertir en un objeto DateTime usando los métodos Parse (), ParseExact (), TryParse () y TryParseExact ().

Los métodos Parse () y ParseExact () generarán una excepción si la cadena especificada no es una representación válida de una fecha y hora. Por lo tanto, se recomienda usar el método TryParse () o TryParseExact () porque devuelven falso si una cadena no es válida. 

Veamos un ejemplo: