Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
sábado, 28 de diciembre de 2019
Un reciente estudio de la consultora Garner indica que durante el desarrollo de una aplicación dedicamos más del 80% de nuestro tiempo a implementar controles de posibles fallos.

Además, este otro informe de StackOverflow obtenido tras analizar el código fuente de miles de proyectos open source, el control y tratamiento de excepciones y problemas supone más del 60% de nuestra base de código y, por tanto, aporta gran parte de la complejidad interna de las aplicaciones.

Pero, adicionalmente, estos estudios ponen al descubierto otros tres aspectos bastante interesantes:
  • Primero, que la mayoría de errores que intentamos controlar no se van a producir nunca. Son posibles a nivel de flujo de código, pero en la operativa de la aplicación no ocurrirán, por lo que podríamos decir que son problemas creados artificialmente durante el proceso de desarrollo.
     
  • Segundo, las líneas de control de errores no están exentas de problemas, por lo que muy a menudo encontraremos en ellas nuevo código de control (¿quién no ha visto try/catch anidados a varios niveles?), por lo que la bola de nieve no para nunca de crecer: código de tratamiento de errores que a su vez contiene código de tratamiento de errores, y así hasta el infinito.
     
  • Y por último, también nos encontramos con que en muchas ocasiones el código de control no hace nada. Por ejemplo, se cuentan por millones las líneas de código detectadas en Github cuyo tratamiento de excepciones consiste simplemente en la aplicación a rajatabla del Swallow Design Pattern, por ejemplo, implementando bloques catch() vacíos.
Y conociendo estos datos, ¿por qué dedicamos tanto tiempo a la gestión de errores en nuestro código? Pues básicamente porque hemos sido educados para eso. Exceptuando cuando se nos cala el coche, no hay nada que suponga un golpe al ego tan importante como cuando una de nuestras aplicaciones falla, y por eso no escatimamos recursos a la hora de evitarlo.

¿No estaría bien poder ignorar esos problemas y centrar nuestro código en aportar valor a nuestros clientes?

Introducing GoF's Ostrich Design Pattern (ODP)

Design Patterns Book Si, como el que os habla, sois unos seguidores incondicionales del Gang of Four y sus 23 patrones de diseño sabréis que el número 18 es el denominado Ostrich Pattern. Se trata de un patrón de diseño que describe una fórmula para el tratamiento de errores bastante más productiva y eficiente que la que solemos utilizar.

Básicamente, su enunciado es:
Para aumentar la productividad y evitar que los errores provoquen problemas de estabilidad y funcionamiento, lo más recomendable es ignorarlos.
O en otras palabras, ignorar los errores y no hacer nada con ellos es mejor tanto desde el punto de vista de la productividad como de la fiabilidad de las aplicaciones, pues:
  • No tendremos que perder tiempo en introducir código de control de errores.
  • La complejidad extra que debemos añadir a una aplicación para gestionar todos los posibles errores introduce un riesgo de fallo superior al que estamos intentando combatir.
  • Y, además, dado que la mayoría de estos errores no van a producirse nunca, el impacto de no controlarlos será prácticamente cero.
En la práctica, la aplicación de este patrón es bastante sencilla... y de hecho, llevamos décadas haciéndolo.

Los que trabajásteis con aquella maravilla prehistórica llamada QuickBasic, con las primeras versiones de Visual Basic (¡sí, cuando aún no llevaba el apellido .NET!), con su hermano pequeño VBScript, con VBA (Visual Basic for Applications) o incluso con Visual Basic .NET, seguro disfrutasteis de la seguridad y tranquilidad de introducir una sentencia como la siguiente:
On Error Resume Next
' Hacer algo que puede que falle
La sentencia On Error Resume Next es la implementación más conocida del patrón de diseño Ostrich.
On Error Resume Next simplemente indicaba al runtime que si se producía un error o excepción debía ignorarla y seguir ejecutando la siguiente instrucción. El siguiente ejemplo ilustra la diferencia entre usarlo o no hacerlo:
Sub Main(args As String())
    Ostrich()
    Classic()
End Sub

Sub Ostrich()
    On Error Resume Next
    DoSomethingWrong()
    Console.WriteLine("This message will be shown")
End Sub

Sub Classic()
    DoSomethingWrong()
    Console.WriteLine("This message will never be shown")
End Sub

Private Sub DoSomethingWrong()
    Throw New Exception("Boom!")
End Sub

Soporte ODP en C# 9

Seguro que estáis al tanto de que el equipo de diseño de C# está haciendo grandes esfuerzos por evitar los errores que más habitualmente encontramos en nuestras aplicaciones.

Prueba de ello es la reciente introducción de los tipos referencia anulables, que pueden ayudar a evitar en gran medida los recurrentes null reference exceptions. Sin embargo, ¿qué pasa con el resto de errores? Y aquí es donde entra el nuevo soporte Ostrich nativo en la próxima versión de C#.

Ha habido bastante discusión al respecto en Github, pero existe ya un cierto acuerdo en que la sintaxis estará alineada con tecnologías existentes con objeto de facilitar su adopción. Por tanto, salvo que se introduzca algún cambio de última hora, podremos activar este nuevo modo permisivo ante errores de la siguiente forma:
public static void Main()
{
    on error resume next; // Ostrich mode on
    DoSomethingWrong();
    Console.WriteLine("This line will be shown");
    var x = 10 / 0;
    Console.WriteLine("This line will be shown")
}

private static DoSomethingWrong()
{
    throw new Exception("Yay!");
}
Como ocurría en otros lenguajes, la simple activación de este modo hará que cualquier excepción sea ignorada y la ejecución continúe por la línea siguiente.
La nueva instrucción on error resume next podrá utilizarse en cualquier ámbito, siendo aplicable exclusivamente al mismo. En el ejemplo anterior, el patrón ODP sólo se aplicaría en el interior del método Main(), pero el siguiente código muestra cómo podría ser aplicado a una clase completa:
public class InvoiceServices
{
    on error resume next; // Ostrich mode in this class
    public void AddInvoice(Invoice invoice);
    public void RemoveInvoice(int id);
    ...
}
También puede ser aplicado a nivel de namespace:
namespace MyApp.Services
{
    on error resume next; // Applied to all classes in this namespace
    public class InvoiceServices { ... }
}
O a nivel de proyecto, introduciendo la instrucción fuera de los espacios de nombre:
on error resume next; // Applied to the entire application
namespace MyApp.Services
{
    ...
}

Mecanismos adicionales para gestionar ODP en C#

Lo que hemos visto hasta el momento será lo que habitualmente utilicemos en nuestras aplicaciones. De hecho, se prevé que la mayoría de desarrolladores apliquen on error resume next a nivel global, tal y como ya se está haciendo internamente en el código fuente del propio framework .NET 5.

Sin embargo, aun estando activado el modo de ignoración de errores, puede resultarnos interesante conocer si se ha producido algún problema. Para ello se ha introducido en el runtime la variable global Err, que siempre contendrá el último error producido:
public void MyMethod()
{
    on error resume next;
    var j = 0;
    var i = 10 / j;
    if (Err is DivideByZeroException ex)
    {
        // Hacer algo
    }
}
Aunque se estima que habrá pocas razones para hacerlo, si queremos desactivar el modo Ostrich podemos hacerlo mediante la instrucción On Error Goto 0, de la siguiente forma:
public void MyMethod()
{
    on error resume next; // ODP on
    var j = 0;
    var i = 10 / j;
    Console.WriteLine("This line will be shown");

    on error goto 0;      // ODP off
    var k = i / j;
    Console.WriteLine("This line won't be shown");
}
Seguro que pensaréis que esto del goto 0 es algo extraño, pero está así por retrocompatibilidad y razones históricas, pues era como se hacía tradicionalmente en VB. Personalmente, creo que habría sido más acertado utilizar algo más explícito como on error crash o on error boom, que eran las otras alternativas que fueron consideradas por el equipo de diseño de C#.

En fin, otra de esas pequeñas mejoras que seguirán haciendo de C# el mejor lenguaje de programación, y que podremos comenzar a utilizar a mitad de enero, cuando sean lanzados C# 9, Visual Studio 2020, .NET Core 5, Entity Framework Core 7, LittlePony 2.0 y la primera versión de Web Forms Core.

Publicado en: www.variablenotfound.com.

8 Comentarios:

Rfog dijo...

Solo nos faltan dos más y sabremos cómo funcionan las tres conchas... :-P

Rfog dijo...

¡Amos, no me jodas! Espera, que no había leído la entrada entera. ¿VS 2020 en enero? ¿Ahora releases anuales?

Me bajo de la burra. Me quedo con el 2019 que ya le tengo pillado todo lo que le va mal.

Joseba dijo...

Entiendo que este artículo será otro de los típicos del día de los inocentes ;)

Rfog dijo...

Diox, qué inútil que soy. La que hemos armado en Wintablet con nuestra propia inocentada y voy yo y pico como un gilipuá. Eso me pasa por leer en diagonal.

Anónimo dijo...

Está tan currada la inocentada que me veo años teniendo que escuchar gilipolleces de que no pierda el tiempo detectando errores :(

Jbot dijo...

Y yo buscando el maldito "Patrón ostra" en el número 18 (que es el observable) dentro del libro de los patrones de diseño de GOF... ha sido cuando he llegado a los comentarios cuando me he dado cuenta... pero mira he tenido que pensar sobre ello...

Kiquenet dijo...

El uso de "on error resume next;" no era una mala práctica ?

José María Aguilar dijo...

Qué va, es una práctica buenísima.
(Observa la fecha de publicación del post :D)