C贸mo pas茅 el verano con C # 8

En un lanzamiento reciente del podcast DotNet & More Blazor, NetCore 3.0 Preview, C # 8 y no solo mencionamos casualmente un tema tan candente como C # 8. La historia sobre la experiencia con C # 8 no fue lo suficientemente grande como para dedicarle un tema por separado, por lo que se decidi贸 compartir con ella los medios del g茅nero epistolar.


En este art铆culo, me gustar铆a hablar sobre mi experiencia de usar C # 8 en producci贸n durante 4 meses. A continuaci贸n puede encontrar respuestas a las siguientes preguntas:


  • C贸mo "deletrear" en el nuevo C #
  • Qu茅 caracter铆sticas fueron realmente 煤tiles
  • Que decepcionado

Puede encontrar una lista completa de las caracter铆sticas de C # 8 en la documentaci贸n oficial de Microsoft . En este art铆culo, omitir茅 aquellas oportunidades que no podr铆a probar por una raz贸n u otra, a saber:


  • Miembros de solo lectura
  • Miembros de interfaz predeterminados
  • Estructuras de referencia desechables
  • Flujos asincr贸nicos
  • 脥ndices y rangos

Propongo comenzar con una de las posibilidades m谩s deliciosas, como me pareci贸 antes.


Cambiar expresiones


En nuestros sue帽os, presentamos esta funci贸n muy optimista:


int Exec(Operation operation, int x, int y) => operation switch { Operation.Summ => x + y, Operation.Diff => x - y, Operation.Mult => x * y, Operation.Div => x / y, _ => throw new NotSupportedException() }; 

Pero, desafortunadamente, la realidad hace sus propios ajustes.
En primer lugar, no hay posibilidad de combinar las condiciones:


  string TrafficLights(Signal signal) { switch (signal) { case Signal.Red: case Signal.Yellow: return "stop"; case Signal.Green: return "go"; default: throw new NotSupportedException(); } } 

En la pr谩ctica, esto significa que en la mitad de los casos, la expresi贸n del interruptor tendr谩 que convertirse en un interruptor regular para evitar copiar y pegar.


En segundo lugar, la nueva sintaxis no admite declaraciones, es decir c贸digo que no devuelve un valor. Parecer铆a, bueno, y no es necesario, pero yo mismo me sorprend铆 cuando me di cuenta de la frecuencia con la que se usa el interruptor (junto con la coincidencia de patrones) para una afirmaci贸n en las pruebas.


En tercer lugar, la expresi贸n switch, que se desprende del 煤ltimo p谩rrafo, no admite manejadores de l铆neas m煤ltiples. Qu茅 miedo entendemos al momento de agregar los registros:


  int ExecFull(Operation operation, int x, int y) { switch (operation) { case Operation.Summ: logger.LogTrace("{x} + {y}", x, y); return x + y; case Operation.Diff: logger.LogTrace("{x} - {y}", x, y); return x - y; case Operation.Mult: logger.LogTrace("{x} * {y}", x, y); return x * y; case Operation.Div: logger.LogTrace("{x} / {y}", x, y); return x / y; default: throw new NotSupportedException(); } } 

No quiero decir que el nuevo interruptor es malo. No, 茅l es bueno, simplemente no lo suficientemente bueno.


Propiedad y patrones posicionales


Hace un a帽o, me parec铆an los principales candidatos para el t铆tulo de "oportunidad que cambi贸 el desarrollo". Y, como se esperaba, para utilizar todo el poder de los patrones posicionales y de propiedad, debe cambiar su enfoque de desarrollo. Es decir, es necesario imitar los tipos de datos algebraicos.
Parece que cu谩l es el problema: toma la interfaz del marcador y listo. Desafortunadamente, este m茅todo tiene un serio inconveniente en un proyecto grande: nadie garantiza el seguimiento en tiempo de dise帽o de la expansi贸n de sus tipos algebraicos. Por lo tanto, es muy probable que con el tiempo, los cambios en el c贸digo conduzcan a muchos "fallos en el incumplimiento" en los lugares m谩s inesperados.


Patrones de tuplas


Pero el "hermano menor" de las nuevas posibilidades de comparaci贸n con la muestra demostr贸 ser realmente bien hecho. La cuesti贸n es que el patr贸n de tupla no requiere ning煤n cambio en la arquitectura familiar de nuestro c贸digo, simplemente simplifica algunos casos:


  Player? Play(Gesture left, Gesture right) { switch (left, right) { case (Gesture.Rock, Gesture.Rock): case (Gesture.Paper, Gesture.Paper): case (Gesture.Scissors, Gesture.Scissors): return null; case (Gesture.Rock, Gesture.Scissors): case (Gesture.Scissors, Gesture.Paper): case (Gesture.Paper, Gesture.Rock): return Player.Left; case (Gesture.Paper, Gesture.Scissors): case (Gesture.Rock, Gesture.Paper): case (Gesture.Scissors, Gesture.Rock): return Player.Right; default: throw new NotSupportedException(); } } 

Pero la mejor parte es que esta caracter铆stica, que es bastante predecible, funciona muy bien con el m茅todo Deconstruct. Simplemente pase una clase con Deconstruct implementado para cambiar y usar las capacidades del patr贸n de tupla.


Usando declaraciones


Parecer铆a una oportunidad menor, pero trae mucha alegr铆a. En todas las promociones, Microsoft habla sobre un aspecto como reducir la anidaci贸n. Pero seamos honestos, no tanto lo que importa. Pero lo realmente serio son los efectos secundarios de excluir un bloque de c贸digo:


  • A menudo, al agregar el uso, tenemos que extraer el c贸digo "dentro" del bloque usando el m茅todo de copiar y pegar. Ahora simplemente no lo pensamos
  • Las variables declaradas dentro del uso y usadas despu茅s de Eliminar el objeto que usa son un verdadero dolor de cabeza. Un problema menos
  • En las clases que requieren llamadas frecuentes de Dispose, cada m茅todo ser铆a 2 l铆neas m谩s largo. Parecer铆a un poco, pero en la condici贸n de muchos m茅todos peque帽os, este poco no permite mostrar una cantidad suficiente de estos m茅todos en una pantalla

Como resultado, algo tan simple como usar declaraciones cambia la sensaci贸n de codificaci贸n tanto que simplemente no desea volver a c # 7.3.


Funciones locales est谩ticas


Para ser honesto, si no fuera por la ayuda del an谩lisis de c贸digo, ni siquiera notar铆a esta posibilidad. Sin embargo, se estableci贸 firmemente en mi c贸digo: despu茅s de todo, las funciones locales est谩ticas son perfectamente adecuadas para el papel de peque帽as funciones puras, ya que no pueden soportar el cierre de las variables del m茅todo. Como resultado, es m谩s f谩cil para el coraz贸n, porque comprende que hay un error potencial menos en su c贸digo.


Tipos de referencia anulables


Y para el postre, me gustar铆a mencionar la caracter铆stica m谩s importante de C # 8. En verdad, el an谩lisis de tipos de referencia anulables merece un art铆culo separado. Solo quiero describir las sensaciones.


  • En primer lugar, es maravilloso. Podr铆a haber descrito previamente mi intenci贸n expl铆cita de declarar un campo o propiedad anulable, pero ahora esta funci贸n est谩 integrada en el lenguaje.
  • En segundo lugar, no guarda nada de NullReferenceException. Y no estoy hablando de la notoria "obstrucci贸n" en las advertencias. Es solo que en tiempo de ejecuci贸n nadie genera ninguna comprobaci贸n de argumento nulo para usted, as铆 que no se apresure a lanzar c贸digo como lanzar nuevo ArgumentNullException ()
  • En tercer lugar, hay un problema grave con el DTO. Por ejemplo, anota una propiedad con el atributo Requerido. En consecuencia, un objeto con una propiedad 100% no nula entrar谩 en su controlador WebAPI. Sin embargo, no es posible asociar este atributo y todos los atributos similares con comprobaciones de tipos de referencia anulables. El caso es que si declaras MyProperty est谩ndar {get; set;} una propiedad con un tipo NotNull, recibir谩 una advertencia: "[CS8618] La propiedad 'MyProperty' no anulable no est谩 inicializada. Considere declarar la propiedad como anulable" . Lo cual es justo, ya que no puede garantizar una sem谩ntica no nula durante el proceso de inicializaci贸n. El 煤nico resultado de esta caracter铆stica es la incapacidad de usar propiedades no nulas en cualquier DTO. Pero hay buenas noticias, hay una soluci贸n simple: simplemente inicialice su campo con el valor predeterminado:
     public string MyProperty { get; set; } = ""; 
  • Cuarto, los atributos que manejan casos complejos, como TryGetValue, son bastante complejos. Como resultado, es muy probable que los desarrolladores poco conscientes abusen de los operadores (!), Nivelando as铆 las capacidades de los tipos de referencia anulables. Una esperanza para los analizadores.
  • Quinto, y lo m谩s importante, personalmente esta oportunidad ya me ha salvado muchas veces de los errores de NullReferenceException. Resulta un ahorro de tiempo banal: se detectan muchos errores en la etapa de compilaci贸n, y no pruebas o depuraci贸n. Esto es especialmente cierto no solo en el proceso de desarrollar una l贸gica empresarial compleja, sino tambi茅n en el caso de un trabajo trivial con bibliotecas externas, DTO y otras dependencias, que posiblemente contengan valores nulos.

Resumen


Por supuesto, las oportunidades presentadas no alcanzan una revoluci贸n completa, pero cada vez hay menos brecha entre C # y F # / Scala. Ya sea bueno o malo, el tiempo lo dir谩.


En el momento del lanzamiento de este art铆culo, C # 8 puede que ya se haya asentado en su proyecto, por lo que me pregunto, 驴qu茅 siente sobre la nueva versi贸n de nuestro idioma favorito?

Source: https://habr.com/ru/post/467655/


All Articles