Como decíamos hace unos días, los generadores de código C# nos brindan la posibilidad de crear al vuelo código C# e incluirlo en nuestros proyectos en tiempo de compilación.
Por no alargar demasiado el post, vimos un sencillísimo ejemplo de implementación, pero ahora vamos a crear algo más complejo que podría ayudarnos a solucionar un problema que tendría difícil solución de no contar con esta característica del compilador.
1. Definición de objetivos
El reto al que vamos a enfrentarnos ya lo expusimos en el post anterior como un caso de uso simple de los generadores de código, así que vamos a reproducir la descripción del escenario.
Imaginemos que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator
, MultiplyOperator
, DivideOperator
, SubtractOperator
. Imaginad también que nos interesa tener un tipo enum Operators
donde aparezca un miembro por cada operador disponible, algo como:
public enum Operators
{
Sum,
Multiply,
Divide,
Subtract
}
El problema que tiene enfocar esto de forma manual es que resultaría sencillo implementar una nueva clase operador y olvidar crear su correspondiente entrada en la enumeración Operators
. Aquí es donde vienen al rescate los generadores de código :)
Lo que implementaremos hoy es un generador de código C# que creará la enumeración por nosotros en tiempo de compilación, manteniéndola sincronizada en todo momento con las clases que tengamos definidas en el proyecto. Para ello, crearemos un generador llamado OperatorsEnumGenerator
que:
- En la fase de análisis de código recopilará las clases del proyecto a compilar cuyo nombre finalice por
Operator
. - En la fase de generación de código creará el enum con los miembros registrados anteriormente.
¡Vamos allá!
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, c#, generadores, metaprogramación
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Componentes genéricos (templated components) en Blazor
José María Aguilar - Redirecciones HTTP 303, 307 y 308: ¿las conoces?
José María Aguilar
.NET Core / .NET
- Announcing .NET 6 Preview 2
Richard Lander - ICYMI C# 8 New Features: Upgrade Interfaces Without Breaking Existing Code
Jason Roberts - First look at InferSharp: A C# version of Facebook’s Infer
Neel Bhatt - Language detection and words-in-sentence classification in C#
Dan - Getting started with Dapr for .NET Developers
Laurent Kempé - How to map IPs to country for free with .NET and IP2Location
Thomas Ardal - Generating Date of Birth values for .NET5.0 Record types using AutoFixture
Adam Storr - AutoWrapper 4.5.0 Released!
Vincent Maverick Durano - Unexpected finding about “await using var”
Jiří Činčura - Download the right ChromeDriver version & keep it up to date on Windows/Linux/macOS using C# .NET
Niels Swimberghe - Blinking LEDs with Raspberry Pi
Richard Lander - My Favorite C# Features Part 2: LINQ
Jeffrey T. Fritz - Five C# Features You Might Not Know
Andrea Chiarelli - ConfigureAwaitChecker with support for “await using” and “await foreach”
Jiří Činčura - 6 free tools for .NET developers
Thomas Ardal - C# 9.0: Covariant Return Types – Specify More Specific Return Types in Overridden Methods and Properties
Thomas Claudius Huber - Investigating a Linux CVE with .NET Images
Richard Lander
Seguramente muchos coincidiremos en que una de las novedades más interesantes de la última versión del compilador de C# es lo que oficialmente han denominado C# Source Generators, o generadores de código fuente de C#.
Muy resumidamente, esta característica añade un nuevo paso en la compilación en el cual los desarrolladores podemos introducir componentes propios (generadores) que inspeccionen el código de la aplicación que está siendo compilada y generen nuevos archivos, que a su vez pueden ser compilados e incluidos en los ensamblados resultantes. Su objetivo, tal y como se declara en su documento de diseño, es posibilitar la metaprogramación en tiempo de compilación.
Veámoslo con un ejemplo donde, además de explicarlo mejor, se puede mostrar su utilidad. Imaginad que en nuestra aplicación tenemos clases que representan operadores matemáticos como SumOperator
, MultiplyOperator
, DivideOperator
, SubtractOperator
, y todos ellos heredan de una clase base Operator
. Imaginad también que nos interesa tener un tipo enumerado enum Operators
donde aparezca un miembro por cada operador disponible, algo como:
public enum Operators
{
Sum,
Multiply,
Divide,
Subtract
}
Muy probablemente os habéis encontrado alguna vez con un escenario similar y habéis sufrido la dificultad de mantener sincronizada la enumeración con las clases que heredan de Operator
: cada vez que aparezca un operador nuevo e implementemos la clase operador que lo representa, tendremos que acordarnos de ir a Operators
y añadir el miembro.
Pues bien, aunque simple, esto sería un caso de uso bastante claro para los generadores de código fuente de C#. Gracias a ellos, podríamos crear un componente generador que examine nuestro código en busca de herederos de Operator
y genere al vuelo, siempre en tiempo de compilación, un archivo de código con la enumeración Operators
.
A todos los efectos, es como si esa enumeración la hubiéramos escrito a mano, porque podremos usarla con normalidad, aparecerá en intellisense, etc., pero la diferencia es que será generada cada vez que compilemos el proyecto, asegurando así que siempre será correcta y completa.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, c#, generadores, metaprogramación, novedades
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Extendiendo claims de usuarios en ASP.NET Core
José María Aguilar - Cómo invocar métodos de instancia C# desde Javascript con Blazor (interop 3/3)
José María Aguilar
.NET Core / .NET
- Ofuscación: cómo proteger el código de tus aplicaciones .NET
José Manuel Alarcón - How to Scan NuGet Packages for Security Vulnerabilities
Drew Gillies - TasKCompletionSource for I/O-Bound Operations
Mehdi Hadeli - Generate iCal calendar with .NET using iCAL.NET
Kristoffer Strube - State of the NuGet Ecosystem
Jiachen Jiang - Strongly-typed Ids using C# Source Generators
Gérald Barré - ICYMI C# 8 New Features: Prevent Bugs with Static Local Functions
Jason Roberts - Refactoring with reflection
Michał Białecki - The 8 most missing features in C#
Konrad Kokosa - C# Coding Standards – Updated
Jesse Liberty
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)
Por si te lo perdiste...
- Cómo solucionar el error "Unable to connect to web server 'IIS Express'" en Visual Studio
José María Aguilar - Renderizar una vista Razor a un string en ASP.NET Core MVC
José María Aguilar
.NET Core / .NET
- Free eBook: How to use Dapr for .NET Developers
Scott Hanselman - ICYMI C# 8 New Features: Simplify Array Access and Range Code
Jason Roberts - Experimenting with .NET 5 and 6 using Docker containers
Elton Stoneman - Multi-targeting a Roslyn analyzer
Gérald Barré - Generating HTTP API clients using Visual Studio Connected Services
Jon Galloway - How can I prevent a Windows Runtime WebView from loading any content beyond the initial request?
Raymond Chen - .NET 6 – Single file apps improved for Windows and Mac!
Bruno Capuano - How to create faster and smarter apps with .NET 5
Michael Crump - C#'s Functional Journey
Mads Torgersen - Using Streams with HttpClient to Improve Performance and Memory Usage
Marinko Spasojevic - Internals of the POH
Maoni Stephens - How to Debug Live .NET Application
Hasan Hasanov - C# 9.0: Pattern Matching in Switch Expressions
Thomas Claudius Huber
Ya hace tiempo que se lanzó .NET 5, pero seguro que algunos os habéis dado cuenta de que cuando desde Visual Studio creamos determinados tipos de proyecto, como bibliotecas de clases o proyectos de consola, por defecto éstos utilizan como target .NET Core 3.1 en lugar de .NET 5.
No se trata de un error; desde Microsoft justifican esta decisión porque .NET 5 no es una versión LTS, y prefieren que por defecto los proyectos sean creados usando una versión con mayor tiempo de soporte, como .NET Core 3.1.
Esto tiene fácil solución, porque tras crearlo simplemente deberíamos acceder a las propiedades del proyecto o editar el archivo .csproj
y modificar ajustar el target framework a nuestro antojo, pero, ¿cómo podríamos evitar tener que hacer esto cada vez?