Azúcar sintáctico
Lo cierto es que C# se encuentra en un estado bastante lejos de una estabilidad, lo que se esperaría despues de casi 25 años (la primera distribución fue en 2000).
Mucho de estas nuevas características no son realmente más que azúcar sintáctico. El azúcar sintáctico, o tambiénsyntactic sugar, consiste en modificar el compilador para que actúe como una especie de preprocesador de nuevas características, generando código ya previamente aceptado.
Tomemos por ejemplo la siguiente clase, que representa una tarjeta bancaria:
usingSystem;usingSystem.Diagnostics;usingSystem.Collections.Generic;publicclassTarjeta{publicTarjeta(intnum,DateOnlycaduca){Debug.Assert(num>0,"num. de tarjeta debe ser positivo");Debug.Assert(caduca>DateOnly.FromDateTime(DateTime.Now),"caducidad debe ser a futuro");this.Num=num;this.Caduca=caduca;this.calculaCVC();}publicintNum{get;privateset;}publicDateOnlyCaduca{get;privateset;}publicintCVC{get;privateset;}privatevoidcalculaCVC(){this.CVC=this.Num%1000;}publicoverridestringToString(){return$"{this.Num} ({this.Caduca},{this.CVC})";}}
Podríamos crear un objeto de esta clase convar t1 = new Tarjeta( 548901687, new DateOnly( 2034, 11, 5 ) );
, y si visualizamos por consola este objeto obtendríamos548901687 (11/5/2034, 687)
. Nótese que es un ejemplo en el que hemos simplificado mucho.
Esta claro cuáles son las invariantes de esta clase: el número de la tarjeta, el CVC y la caducidad, no varían una vez asignados; el CVC se calcula a partir de la numeración; y finalmente la caducidad tiene que estar en el futuro.
La nueva característica son las propiedades de inicialización (init), que pueden inicializarse en el momento de creación del objeto. Podemos marcarlas también como requeridas (required), de forma que, si no se aportan en el momento de creación del objeto, se produce un error de compilación.
Por ejemplo, la propiedadNum, la numeración de la tarjeta podría pasar a ser la siguiente:
classTarjeta{publicrequiredintNum{get;init;}// más cosas...}
Así que la inicialización del objeto seríavar t1 = new Tarjeta { Num = 548901687 };
. Quizás se podría argumentar que la inicialización es más explícita, ya que obligatoriamente se menciona el nombre de la propiedad (al llamar al constructor se puede mencionar el nombre del argumento, pero ni es obligatorio ni el parámetro tiene que tener un nombre significativo).
Está claro que no es más que la creación de un método inicializador del objeto por parte del compilador en el que se realizan estas asignaciones.
Probablemente, si nos parece interesante, nos dediquemos con entusiasmo a portar nuestro código sobre la claseTarjeta.
publicclassTarjeta{publicrequiredintNum{get;init;}publicrequiredDateOnlyCaduca{get;init;}publicintCVC{get;privateset;}privatevoidcalculaCVC(){this.CVC=this.Num%1000;}publicoverridestringToString(){return$"{this.Num} ({this.Caduca},{this.CVC})";}}
Fantástico. Ahora podemos crear objetos de la tarjeta solo con el siguiente código:
vart1=newTarjeta{Num=548901687,Caduca=newDateOnly(2034,11,5)};Console.WriteLine(t1);
Genial, ¡somos modernos!
Pero si analizamos un poco el código, veremos que, en realidad, hemos perdido la mayor parte de invariantes. Las propiedades pueden ser inmutables, pero no hay forma de comprobar que la numeración sea un entero positivo. ¡Tampoco estamos calculando el CVC!
Vale, no pasa nada. Vamos a modificar las propiedades para poder reestablecer estas invariantes.
publicclassTarjeta{publicrequiredintNum{get=>this._num;init{Debug.Assert(value>0,"num. de tarjeta debe ser positivo");this._num=value;}}publicrequiredDateOnlyCaduca{get=>this._caduca;init{Debug.Assert(value>DateOnly.FromDateTime(DateTime.Now),"caducidad debe ser a futuro");this._caduca=value;}}publicintCVC{get;privateset;}publicTarjeta(){this.calculaCVC();}privatevoidcalculaCVC(){this.CVC=this.Num%1000;}publicoverridestringToString(){return$"{this.Num} ({this.Caduca},{this.CVC})";}privateint_num;privateDateOnly_caduca;}
Ahora sí. Volvemos a cumplir todos los compromisos de la clase. Creamos nuestro objeto, lo visualizamos y...
vart1=newTarjeta{Num=548901687,Caduca=newDateOnly(2034,11,5)};Console.WriteLine(t1);// 548901687 (11/5/2034, 0)
¿Un cero como CVC? La única forma de que elCVC pueda llegar a ser 0 es que a su vezNum sea también 0. Está claro que la numeraciónNum todavía no se ha asignado a su valor. Podemos deducir que el código generarado por el compilador para la creacióndet1 más arriba sería algo como:
classTarjeta{publicTarjeta(intnum,DateOnlycadunca){this.calculaCVC();this.__init(num,caduca);}privatevoid__init(intnum,DateOnlycaduca){this.Num=num;this.Caduca=caduca;}publicintNum{get;privateset;}publicDateOnlyCaduca{get;privateset;}publicintCVC{get;privateset;}// más cosas...}vart1=newTarjeta(num:548901687,caduca:newDateOnly(2034,11,5));
Por eso se le llama azúcar sintáctico, porque no es que se aporte una sintaxis nueva soportando una nueva construcción expresiva, sino que el compilador genera código ya conocido.
La llamada al inicializador que el compilador inyecta al final del constructor, se produce por tanto después de cualquier código ejecutable en dicho constructor. No tenemos oportunidad de hacer nada con los valores a las propiedades ya asignadas.
Así que está claro que no es bueno mezclar propiedades de inicialización y constructores. O al menos, no si el código del constructor depende de los valores que se van a inicializar mediante esas propiedades.
Así que si no podemos depender del constructor, ni para calcular el CVC ni para garantizar las invariantes, tenemos que hacer cambios.
publicclassTarjeta{publicrequiredintNum{get=>this._num;init{Debug.Assert(value>0,"num. de tarjeta debe ser positivo");this._num=value;this.calculaCVC();}}publicrequiredDateOnlyCaduca{get=>this._caduca;init{Debug.Assert(value>DateOnly.FromDateTime(DateTime.Now),"caducidad debe ser a futuro");this._caduca=value;}}publicintCVC{get;privateset;}// más cosas...privateint_num;privateDateOnly_caduca;}
Bueno, pues ahora sí que ya podemos inicializar las propiedades en lugar de llamar al constructor ("a la antigua"). ¿Merece la pena?
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse