Archivo de la etiqueta: .net

C# 5.0: Nuevas características

Esta “nueva” versión (lanzada en Agosto del 2012) de C# incluido en .NET Framework 4.5, incluye algunas novedades que voy a mencionar a continuación, pero que se basa fundamentalmente en la implementación del “asincronismo” de una forma mucho más sencilla que con sus predecesores, ya que a la hora de trabajar con Multithreading no deberemos tirar muchas más líneas de código con respecto a la programación síncrona/secuencial de toda la vida.

Evolución de C#

“Async” o asincronismo más sencillo

Como he comentado anteriormente, esta característica representa el groso de la nueva versión que Microsoft publicó con respecto a su lenguaje de programación estrella, C# 5.0.

La API para programar asíncronamente es sencilla de entender, ya que todos aquellos que hayan estudiado un poco sobre el tema enseguida le sacarán parecido a otras implementaciones de lenguajes de programación como JAVA, C, NodeJS…

En el apartado de programación asíncrona, vamos a manejar un ejemplo sencillo con la clase WebClient, que nos permitirá descargar un documento de manera asíncrona dándole al usuario la posibilidad de poder realizar otro tipo de acciones mientras esta tarea se resuelve, además me parece muy interesante porque no solo se puede aplicar a aplicaciones de escritorio (WinForms), sino que también es aplicable a Web (ya sea WebForms o MVC).

Esto para las aplicaciones de escritorio es un problema muy común, ya que al tardar demasiado tiempo Windows se impacienta y notifica que la aplicación “no responde”, por tanto se bloquea y damos una imagen al usuario bastante pobre de nuestro nivel como programadores.

API Síncrono: WebClient.DownloadString

API Asíncrono: WebClient.DownloadStringAsync

Con el uso de este método solventamos el problema de bloqueo de procesos, de forma transparente para el programador y dotar así al usuario de una mayor flexibilidad a la hora de interaccionar con nuestra aplicación.

Muy bien, ya tenemos una solución a nuestro problema de bloqueo de procesos, pero ahora nos surge otro problema, no sabemos cuando se finaliza ese proceso al que estamos esperando, y por tanto ejecutarlo en el hilo/proceso correspondiente para evitar posibles excepciones que nos pueda indicar nuestra pila con un mensaje esclarecedor de que estamos en una ejecución fuera de contexto.

WebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(WebClient_DownloadStringCallback);
public void WebClient_DownloadStringCallback (object sender, DownloadStringCompletedEventArgs e)
{
   this.txtResult.Text = e.Result;
}

Para solucionar el problema explicado anteriormente, tendremos un manejador de eventos dedicado especialmente para rescatar el resultado de la llamada asíncrona que hemos realizado, así de una forma muy sencilla podremos acceder al hilo que quedó a la espera de la descarga del documento que solicitamos previamente. Más adelante os mostraré los hilos de ejecución que tenemos disponibles y así hacernos una idea por donde transcurre el programa.

Una vez entendemos cómo realizar una llamada asíncrona, vamos a ver como se hacía antes y como lo haríamos ahora en .NET. En el caso del código de servidor existe una clase llamada SynchronizationContext que soluciona este problema en versiones anteriores (nos ayuda a manejar las requests” y las “responses” pertenecientes a un mismo contexto/hilo) funciona con ASP.NET, WPF, Win Forms…

Antes de C# 5.0

A continuación veremos con un ejemplo sencillo, cómo obtener de forma asíncrona los headers presentes actualmente en una web HttpWebRequest, si queremos obtener solamente los headers incluimos en:  HttpWebRequest.Method = “HEAD”  y a continuación implementamos el siguiente código:

HttpWebRequest.BeginGetResponse(
      asyncResult => {
            var resp = (HttpWebResponse) HttpWebRequest.EndGetResponse(asyncResult);
            string headersText = FormatHeaders (resp.Headers);
            this.txtResult.Text = headersText;
      },
      null);

Como vemos, para la llamada BeginGetResponse debemos llamar a un delegado que será el encargado de continuar con la ejecución una vez se ha completado la “request”, en este caso la obtención de los headers y almacenarlos en una caja de texto. También cabe mencionar que como último parámetro es posible incluir cierta información arbitraria, dicha información podría servirnos como añadido a nuestra llamada asíncrona y utilizarla para tal efecto.

En este primer ejemplo, se verá como obtenemos una excepción ya que intentamos asignar los headers de la respuesta a la caja de texto en un hilo distinto al que deberíamos, Main Thread sería el encargado de hacer esta tarea y en cambio es otro hilo secundario el que lo hace…

Threads Asíncronismo
Threads Asíncronismo

Por lo tanto, para solucionar este problema debemos utilizar la clase SynchronizationContext para poder asignar en su delegado dentro del evento BeginGetResponse el resultado a la caja de texto, a continuación veríamos como queda el código:

  var sync = SynchronizationContext.Current;
  HttpWebRequest.BeginGetResponse(
        asyncResult => {
               var resp = (HttpWebResponse) HttpWebRequest.EndGetResponse(asyncResult);
               string headersText = FormatHeaders (resp.Headers);
                   sync.Post(
                        delegate {
                             this.txtResult.Text = headersText;
                        },
                   null);
        },
  null);

Con este código tenemos el comportamiento normal de Multithreading, donde el hilo padre continúa su ejecución de forma normal hasta que finaliza (queda dormido, esperando a sus hijos, en este caso asyncResult), esto lo hacemos gracias al método Post que contiene la clase SynchronizationContext, tal y como entendemos por su nombre se encarga de capturar la finalización de la petición en su hilo correspondiente (padre) para terminar la ejecución del mismo, de esta forma podemos evitar posibles errores de contexto en la ejecución de nuestro programa.

Nota: Para aquellos que tengan un poquito más de conocimiento sobre el tema, deciros que ya no peligra el famoso estado de “inanición” de nuestros procesos gracias a la implementación del asincronismo facilitado por .NET Framework 4.0.

Resumiendo, antes con C# 4.0 era necesario implementar hasta 3 diferentes métodos para el correcto funcionamiento del asincronismo, eso sí de forma muy sencilla y aplicando el patrón de eventos asíncronos, comúnmente conocido como EAP ya disponible en la versión 2.0 de .NET.

Ahora con C# 5.0

Para empezar a trabajar con Async debemos añadir una nueva referencia a nuestro proyecto: AsyncCtpLibrary.dll localizada en ..\Microsoft Visual Studio Async CTP\Samples\. Ahora vamos a pasar a la implementación del asincronismo en unos pocos pasos muy sencillos de codificar:

1.  Declarar nuestros eventos asíncronos:

protected async void btnConfirmar_Click(object sender, RoutedEventArgs e)
{
}

Así cualquier desarrollador que retome este código sabrá que este método se utilizará de forma asíncrona en nuestra aplicación, podemos asemejarlo a la inclusión de los atributos [HttpGet] o [HttpPost] en la cabecera de las acciones MVC, indicando que tipo de peticiones atiende nuestro método. Pues aquí lo mismo, pero asociado al tipo de ejecución que se llevará a cabo (síncrono o asíncrono).

2.  Adaptar el código visto anteriormente para que nuestro compilador pueda atenderlo de forma asíncrona:

protected async void btnConfirmar_Click(object sender, RoutedEventArgs e)
{
     WebClient w = new WebClient();
     string txt = await w.DownloadStringTaskAsync("Mi url");
     this.txtResult.Text = txt;
}

La keyword “await” indica al compilador que esta sentencia será un trabajo a desempeñar de forma asíncrona y por tanto deberá tenerlo en cuenta en el momento de ejecución.

Como comentamos anteriormente, esto es muy útil cuando tenemos ciertas peticiones que no se pueden tener esperando más tiempo de lo normal o simplemente nos pueda devolver un 404/timeout, en ese período de tiempo el usuario podrá hacer más cosas mientras espera el resultado del click del botón.

Por tanto, await está preparado para funcionar con TPL (Task Parallel Library) que ya fue incluido en C# 4.0 donde  WebClient.DownloadStringTaskAsync devuelve un System.Threading.Task<string>. Pero claro en el caso de HttpWebRequest o HttpWebResponse no podemos trabajar con TPL tal cual, sino que debemos utilizar el modelo de programación asíncrona mediante la creación de tareas. Esto lo podemos ver en el siguiente ejemplo:

private async void btnConfirmar_Click(object sender, RoutedEventArgs e)
{
    var req = (HttpWebRequest) WebRequest.Create("mi url");
    req.Method = "HEAD";
    Task<WebResponse> getResponseTask = Task.Factory.FromAsync<WebResponse>(req.BeginGetResponse, req.EndGetResponse, null);
    var resp = (HttpWebResponse) await getResponseTask;
    string headersText = FormatHeaders (resp.Headers);
    this.txtResult.Text = headersText;
}

Como vemos en el código anterior,  HttpWebRequest no contiene una sobrecarga de algún método que podamos ejecutar de forma asíncrona (como WebClient), con lo cual debemos utilizar el TaskFactory para crear una tarea que luego posteriormente será tratada como asíncrona con la palabra reservada “await“, para hacer esto siempre debemos indicar la ejecución del código asíncrono y el método que recogerá la finalización de dicha ejecución:

csharp-5-async-3

El único requisito que debemos tener en cuenta, es el tipo de datos que devolvemos para posteriormente tratarlo en nuestro callback de la llamada asíncrona, es decir, si nuestra instancia Task que debe gestionar TaskFactory es de tipo string también debemos pasarle este mismo tipo de datos para no obtener ninguna excepción de “casting“.

Conclusion: C# 5.0 pretende que el asincronismo a nivel de código sea igual de simple que la implementación del sincronismo.

Caller Information (Herramientas de diagnosis y seguimiento)

Otra de las principales novedades que trae la nueva versión de C#, es la posibilidad de usar de forma nativa multitud de herramientas de diagnosis y seguimiento, sin tener que depender de aplicaciones/esamblados “third-party”para solucionar problemas tan cotidianos como la depuración, traceo y la creación de herramientas de diagnosis.

Esto nos ahorra (en el caso de ser un equipo de varios desarrolladores) duplicidad en el código, ya que puede pasar que cada utilice su propio sistema de traceo, depuración de errores… duplicando al fin y al cabo la misma funcionalidad.

Por tanto, Microsoft nos facilita las siguientes clases con información útil para este propósito:

Un ejemplo de como utilizar estos nuevos atributos lo podemos ver a continuación:

public void TracingMethod()
{
   TraceException("ERROR!!");
}

public void TraceException(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
    Trace.WriteLine("Mensaje de error: " + message);
    Trace.WriteLine("Nombre del método: " + memberName);
    Trace.WriteLine("Path del fichero: " + sourceFilePath);
    Trace.WriteLine("Línea de error: " + sourceLineNumber);
}
//Salida:
//  Mensaje de error: ERROR!!
// Nombre del método: TracingMethod
//  Path del fichero: C:\...\Visual Studio 2012\Projects\...\Tracing\CallerInformation.cs
//  Línea de error: 3

Como podéis ver con este método sencillo de implementar, tenemos cubierto cualquier petición de tracing o depuración de errores en nuestra aplicación.

Bueno pues hasta aquí este artículo dedicado especialmente al lenguaje de programación que yo utilizo diariamente, como vemos Microsoft sigue aportando valor a este excelente lenguaje de programación de alto nivel.