Editar y continuar cuando modificamos el ViewBag en MVC

Hace años que podemos editar y continuar en Visual Studio cuando desarrollamos en ASP.Net con el IISExpress y cada día mejoran esta funcionalidad. En Visual Studio 2013 nos simplificaron la tarea habilitando la opción por defecto:
Edit and continue en Web
Aunque nada es perfecto y hay algunas ocasiones en las que editar y continuar no nos va a funcionar: si es async, si hay un lambda, etc. Uno de esos casos lo encontraremos al modificar el objeto ViewBag, pues es un objeto marcado como dynamic y por ahora no se puede modificar y continuar con este tipo de objetos mientras estamos en ejecución:
Edit and continue with viewbag
En este caso, aunque no nos servirá para todos los escenarios, podemos cambiar el objeto desde la Immediate Window e incluso añadir propiedades si es necesario:
Immediate Window dynamic properties

About these ads

Deshabilitar el compilador Roslyn temporalmente

El nuevo compilador de .Net todavía está en CTP y no tiene licencia para desplegar aplicaciones en producción. Si instalas Roslyn en la máquina que utilizas para desarrollar cada día, vas a tener que desinstalarlo antes de compilar código para producción, pues sustituye completamente al compilador de .Net, incluso el de línea de comando.

La instalación y desinstalación de Roslyn es bastante rápida, pero tenemos otra forma de deshabilitar el compilador que nos ahorrará algo de tiempo. Será tan fácil como definir el valor true en la variable DisableRoslyn. Podemos indicar este valor de dos formas: como parámetro de msbuild o como variable de entorno.

Desde Visual Studio podemos definir una propiedad a nivel de proyecto que pasará a msbuild y deshabilitará Roslyn, así:

<PropertyGroup>
  <DisableRoslyn>true</DisableRoslyn>
</PropertyGroup>

Pero tendremos que hacerlo a nivel de cada proyecto y si queremos volver a habilitar Roslyn tendremos que volver a editar todos los proyectos. En realidad es mucho más fácil establecer una variable de entorno llamada DisableRoslyn y asignar un true o un false según nos convenga en cada momento:

Disable Roslyn

Yo me he hecho un .bat y lo he añadido a mi barra de tareas como un botón de activar/desactivar:

@echo off
IF "%DisableRoslyn%"=="true" (
setx DisableRoslyn false
echo Roslyn compilation enabled
) ELSE (
setx DisableRoslyn true
echo Roslyn compilation disabled
)
pause 0

Ahora ya no tenéis excusa para probar Roslyn.

Aunque yo me enteré de esto directamente, ahora es más fácil saber estas cosas: basta mirar el código fuente de http://roslyn.codeplex.com, en este caso veréis la explicación en el archivo CompilerPachage.cs:
CompilerPackage.cs
 

Build 2014

Los nuevos SDKs: Roslyn, SIMD, Windows Phone 8.1, .Net Native…

Build 2014El evento Build de estos días está trayendo una cantidad de novedades en desarrollo que no tenemos tiempo de digerir, hay incluso algunas sorpresas que tenían muy escondidas en el roadmap como la apertura open source del compilador Roslyn.
Ya dedicaré tiempo a explicar y poner ejemplos de cada una de las novedades, por ahora os pongo una lista de lo que me parece más interesante:

 

  • Windows Phone 8.1: ya está disponible el SDK en versión RC. Esta nueva versión se alinea completamente con Windows 8.1, de forma que los proyectos de Phone comparten mucho código con Windows 8.1, aunque necesitaremos dos versiones de la vista XAML para que se adapte al dispositivo. En el caso de apps HTML5/JS muchas veces bastará con cambiar las CSS, si habéis leído bien, HTML5/JS en Windows Phone y es en modo nativo como en las apps de Windows Store. Se puede descargar aquí: https://dev.windowsphone.com/en-us/downloadsdk y el tipo de aplicación para que funcione en ambas plataformas se llama Universal App. Más adelante esto se ampliará a aplicaciones de XBox.
  • Roslyn: los compiladores de C# Y VB se convierten en una plataforma abierta que proporciona una API para que podamos utilizar y/o modificar las partes del compilador que más nos convengan. Esta nueva versión se integra completamente dentro del Visual Studio 2013 Update 2 RC y sustituye al compilador actual. Pero no se han quedado aquí, sino que han publicado Roslyn en Codeplex y ahora está el compilador en código abierto para que podamos ver cómo funciona e incluso proponer cambios. Todo esto se gestionará a través de la .NET Foundation. Si te da pereza descargar el código y compilarlo tu mismo, puedes obtener el nuevo compilador a través de NuGet ejecutando el comando:Install-Package Microsoft.CodeAnalysis -Pre
  • .Net Nativo: Joel Spolsky pidió hace 10 años algo parecido, no debía ser fácil ni prioritario, pero ahora las necesidades de rendimiento y consumo han cambiado y C# ha demostrado ser extremadamente productivo en comparación con otros lenguajes, así que la  compilación nativa de apps va a mejorar el rendimiento de nuestras apps sin que nosotros tengamos que hacer nada! Se puede descargar aquí: http://msdn.microsoft.com/es-es/vstudio/dotnetnative
  • SIMD para aplicaciones .Net: Single Instruction Multiple Data permite paralelizar algunas operaciones a nivel de núcleo. Esta optimización estaba disponible desde C++ y hace tiempo que la comunidad .NET solicitaba que se soportara. A través de la nueva generación del compilador JIT ya podemos probar esta optimización para procesadores con capacidad SSE2, en el siguiente enlace tenéis más información: http://blogs.msdn.com/b/clrcodegeneration/archive/2014/04/03/ryujit-ctp3-how-to-use-simd.aspx
  • .Net para mobile services: con el update 2 RC de Visual Studio 2013 también tendremos la posibilidad de escribir código .Net para mobile services en Azure.
  • Internet de las cosas (…y de las personas, espero): el equipo de Windows Embedded ha mostrado qué cosas podemos hacer con un Windows Embedded 8 y ya está en GA aquí: http://www.microsoft.com/windowsembedded/en-us/windows-embedded-compact-2013.aspx

En definitiva, un montón de cosas nuevas con las que jugar. Yo seguiré con Roslyn, que ahora ya puedo empezar a contar algunas de las cosas que veremos en el Gusenet.

Ver y depurar el código original del .Net Framework ahora es más fácil que nunca

Acaban de anunciar la nueva fuente de referencias del código del .Net Framework que nos permitirá dos cosas principalmente: ver fácilmente cómo están hechas las clases del .Net Framework y además depurar paso a paso el código original.

Aunque Microsoft ya distribuía los archivos .pdb y código fuente desde .Net 4.0, con la nueva filosofía de entregas se hacía bastante difícil de mantener. Ahora gracias al proyecto Roslyn generan todos esos archivos.

Para empezar, tenemos todo el código fuente disponible en la web http://referencesource-beta.microsoft.com, en un formato completamente interactivo, podemos buscar, encontrar todas las referencias a una clase, navegar entre referencias y ver el código fuente, el manejo es realmente espectacular como podréis ver en el vídeo de Channel 9: How to use the net source browser.

vs_httpwebrequest

Además de todo esto podemos depurar directamente desde Visual Studio configurando el entorno, sin necesidad de descargar nada, el propio VS.Net se encargará de descargar los .pdb y código fuente que le haga falta.

vs_debuglist

Tened en cuenta que todavía es una beta y puede dar problemas. Si pasa algo raro siempre podéis borrar el caché de símbolos:
vs_delete_cache

También me ha pasado que el archivo .cs de la clase estaba vacío, supongo que algún error de descarga. Al borrar el archivo de disco Visual Studio ha vuelto a descargarlo y he podido utilizarlo correctamente. Para localizarlo basta pulsar con botón derecho sobre la pestaña con el nombre del archivo.

¡A depurar!

Pruebas unitarias, acceso a ficheros y async para Windows 8

Crash Dummy Label
Cuando desarrollas Apps para Windows 8 u 8.1 y usas pruebas unitarias, en algún momento te encuentras con la necesidad de cargar un archivo para realizar una prueba. Parece una tontería, pero si juntas estas tres cosas puedes estar un buen rato hasta que consigues ponerlo todo en marcha.

Las pruebas unitarias de aplicaciones de la tienda Windows se realizan en proyectos especiales que no tienen acceso a toda la librería .Net, sino al subconjunto que tenemos para Windows RT. Que los test se realicen en un entorno limitado como el de producción es lo ideal, pero nos va a dificultar hacer algunas cosas que solemos hacer fácilmente en otros entornos de test, como por ejemplo leer archivos distribuidos con nuestras pruebas.

Empecemos por ejemplo con el siguiente test:

[TestMethod]
public void TestPlainXmlList()
{
    string xml = "<items><item name='one'/><item name='two'/></items>";
    var testable = new TestableClass();
    IEnumerable<string> items = testable.ListItems(xml);
    Assert.IsNotNull(items);
    Assert.AreEqual(2, items.Count());
}

Si ahora queremos realizar la prueba con un archivo que tenemos dentro del test, tendremos que cargarlo y no tenemos acceso a una clase File para cargar el archivo desde el disco. Tendremos que utilizar un StorageFile del API de Windows 8.

Como habitualmente, vamos a añadirlo al proyecto de pruebas y darle un valor “Copy if newer” en las propiedades del fichero:

Unit Test Files Windows 8

Ahora tenemos que abrirlo desde el test para poder enviar el contenido a nuestro método, los archivos que desplegamos con nuestra aplicación están en Windows.ApplicationModel.Package.Current.InstalledLocation. Es un StorageFolder y lo podemos usar como en cualquier App de Windows 8:

[TestMethod]
public async void TestLoadLargeXml()
{
    string largexml;

    var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
    var file = await folder.GetFileAsync("Files\\test1.xml");
    using (var fileopen = await file.OpenReadAsync())
    {
        using (var stream = fileopen.AsStreamForRead())
        {
            using (var r = new StreamReader(stream))
            {
                largexml = r.ReadToEnd();
            }
        }
    }

    var testable = new TestableClass();
    IEnumerable<string> items = testable.ListItems(largexml);
    Assert.IsNotNull(items);
    Assert.AreEqual(60, items.Count());
}

Como en Windows 8 tenemos muchos métodos asíncronos, hemos tenido que utilizar las palabras async/await pues las operaciones con archivos son asíncronas, pero tal como está escrito el método de test vamos a encontrarnos un problema: no aparecerá el método en el listado de pruebas y no tendremos manera de probarlo.

Los métodos async void no se pueden probar directamente

Esto es así porque un método declarado async void no es awaitable, simplemente se ejecuta en un hilo aparte en modo “fire and forget”, así que el framework de test no podrá saber cuándo ha acabado y lo ignora.
Para que sea awaitable bastará con que el valor de retorno sea una Task en lugar de void, tal que así:

[TestMethod]
public async Task TestLoadLargeXml()

A partir de este momento podremos ejecutar las pruebas sobre nuestros métodos de Windows 8/8.1.
Ahora sólo falta que algún día las pruebas funcionen bien en el VSOnline para tener todo lo que necesitamos para probar nuestras Apps.

Feliz testing!

Patos y código C#

Swimmer duck on Surfboard
Durante los primeros días del año trajo mucha cola un artículo de Eric Lippert, uno de los padres de C#, sobre su visión de qué es el Duck Typing y qué representa para él en lenguajes de tipificado principalmente estático (digo principalmente porque ya sabemos que en .Net podemos hacer muchas cosas raras :P).

En mi opinión, más que criticar el concepto de duck typing, ese artículo es una queja sobre el bajo nivel y falta de coherencia de los artículos de Wikipedia y para demostrar que las modas confunden a la comunidad de desarrolladores. Deja claro que la entrada está mal escrita, usa mal algunos términos y se va contradiciendo según el párrafo.

En cualquier caso, para gustos los colores y yo os voy a contar mi visión al respecto del duck typing. No pretendo rebatir a Eric Lippert, no tengo ni los conocimientos ni la experiencia de Eric y otra gente como él que han hablado sobre el tema, sino que quiero dar mi opinión como desarrollador raso tras unos cuantos años utilizando el concepto en lenguajes como JavaScript y Python. Estoy de acuerdo en que el término es confuso, porque en realidad no tiene que ver directamente con el tipo que tengan los argumentos que enviamos, sino en cómo maneja el desarrollador los valores de los parámetros de sus métodos.

El nacimiento del pato tipificado (con lo bien que sonaba en inglés)

Para los que no conocéis el concepto, la frase que define el duck typing es, traducida, algo así:

Si anda como un pato y parpa como un pato entonces es un pato.

Detrás de esa frase tan simple veremos que hay que leer entre líneas y conocer un poco la historia. La discusión original era sobre cómo manejar el polimorfismo en Python y derivó en si se debía o no comprobar el tipo del objeto que llegaba en un parámetro de función. Los argumentos de Alex Martelli fueron que era mejor dejar de comprobar si el objeto que recibes es exactamente el tipo que te esperabas, sino que si el parámetro tiene las propiedades y métodos que necesitas, utilízalo sin más, si te falta algo lanza una excepción y que se preocupe el que envió al método un objeto inadecuado, todo esto acuñado bajo el símil del pato.

Yo añadiría que el Duck Typing es una convención a la hora de desarrollar porque el que utiliza el método o función debe saber que quien desarrolló ese método está utilizando duck typing, si no están los dos de acuerdo puede ser un auténtico desastre, sobre todo en lenguajes dinámicos (o en porciones dinámicas del código) donde nadie te avisa si estás haciendo algo “ilegal” hasta el momento de su ejecución.
Si pensáis que exagero, imaginad que pasaría si a una función JavaScript que se esperaba un pato le pasáis una bomba:

function vuelaAlto(pato){
    pato.volar(20);
}

var pato= {altura:0, volar: function(dif){this.altura+=dif; console.log(this.altura);}};
var bomba= {altura:0, volar: function(){console.log('¡booom!');},
            lanzar:function(dif){this.altura+=dif; console.log(this.altura);} };

vuelaAlto(pato);
vuelaAlto(bomba);

Aún así, tiene más ventajas que inconvenientes, tanto que, por ejemplo, en JavaScript lo utilizamos constantemente. Basta ver un ejemplo cualquiera de la librería jquery, donde enviamos un objeto anónimo con las propiedades que queremos enviar a la función, sin necesidad de crear una instancia de un tipo en concreto, basta que tenga la forma adecuada:

$.ajax({
  url: "test.html",
  context: document.body
}).done(function() {
  $( this ).addClass( "done" );
});

Duck typing en C#

Uno de los argumentos de Eric es que en realidad Duck Typing se refiere a Late Binding, y eso es algo que siempre hemos tenido en .Net y C#, e incluso en lenguajes más antiguos cuando surgió COM y podíamos ejecutar comandos de un Excel desde un programa hecho en Visual Basic. También dice que en cualquier otro caso tenemos los interfaces y que los lenguajes que lo implementan de forma estática en realidad a eso se le llama Structural Typing, como en Scala, Haskell o GO.

Otros autores opinan que duck-typing es bastante más que eso, por eso he dicho antes que el concepto me parece más una convención que otra cosa, veamos algunos casos que surgen en C# y si creo o no que tienen que ver con duck-typing:

Late Binding

En realidad, late-binding no es duck-typing ni creo que se le parezca. Cuando haces late-binding instancias de forma dinámica, normalmente por el nombre de la clase, un elemento que se corresponde con un tipo en concreto que está en un ensamblado al que no tenías acceso durante la compilación. Luego asignas ese objeto a una variable del tipo que tú sabes manejar y eso sólo puede pasar si el objeto es de ese tipo o hereda del mismo, al contrario que si usamos duck typing donde no tienen por qué ser del mismo tipo, sólo necesitas que se parezcan en las formas.

Normalmente en C# hacemos late-binding usando Reflection con métodos como Activator.CreateInstance(t) y asignando el resultado a un tipo en concreto, mediante un typecasting, de forma que luego nuestro código pueda utilizar ese objeto, por ejemplo:

Assembly assembly = Assembly.Load("ClassLibrary1");
Type type=assembly.GetType("ClassLibrary1.Form1");
Form form = (Form)Activator.CreateInstance(type);
form.Show();

A partir del typecasting con el tipo Form pasamos a utilizar el objeto como lo que es, un Form y si no lo fuera obtendríamos un error en tiempo de ejecución. Aquí está la gran diferencia con duck-typing, pues si utilizáramos este concepto nos bastaría que el objeto tuviera un método Show y no nos haría falta que el objeto heredara de Form, aunque eso sí, eso tiene una penalización de rendimiento.

dynamic

Con la palabra reservada dynamic, que trajo C# 4.0, si que podemos hacer duck-typing, pues nos proporciona la flexibilidad de los lenguajes dinámicos al no hacer typecasting contra ningún tipo concreto, sino que realiza una búsqueda del método o propiedad que nosotros estamos invocando, todo esto en tiempo de ejecución, como cualquier otro lenguaje dinámico. Si os estáis preguntando si es más lento, estáis en lo cierto. En el caso del late-binding sólo ralentiza mientras busca cómo hacer el binding, una vez enlazado el objeto ya se comporta de forma completamente normal, tanto en funcionamiento como en rendimiento.
En el caso de dynamic la penalización de rendimiento es alta y sólo se debería usar si es imposible saber con antelación el tipo.
Es fácil de comprobar si hacemos un ILDASM de una llamada dynamic contra una llamada con interfaz. Veremos cómo la cantidad de código se multiplica por 5, además de acceder al método utilizando un string en lugar de una posición de la tabla virtual.

public class Duck
{
    public string Quack()
    {
        return "Quack!";
    }
}
class Program
{
    static void Main(string[] args)
    {
        var duck = new Duck();
        var ventriloquistDuck = new { Quack = (Func<string>)(() => "Woof") };

        doQuackStatic(duck);

        doQuack(duck);
        doQuack(ventriloquistDuck);

        var notADuck = "Do you quack?";
        try
        {
            doQuack(notADuck);
        }
        catch
        {
            Console.WriteLine("Cannot quack!");
        }
        try
        {
            doQuack(25);
        }
        catch
        {
            Console.WriteLine("Cannot quack!");
        }
    }

    private static void doQuack(dynamic duck)
    {
        Console.WriteLine("Dynamic: {0} says {1}", duck.GetType(), duck.Quack());
    }

    private static void doQuackStatic(Duck duck)
    {
        Console.WriteLine("Static: {0} says {1}", duck.GetType(), duck.Quack());
    }
}

Aquí va el código IL de doQuackStatic tal como me lo da ILDASM tras compilar en modo Release:

.method private hidebysig static void  doQuackStatic(class DuckTyping.Duck duck) cil managed
{
  // Code size       23 (0x17)
  .maxstack  8
  IL_0000:  ldstr      "Static: {0} says {1}"
  IL_0005:  ldarg.0
  IL_0006:  callvirt   instance class [mscorlib]System.Type [mscorlib]System.Object::GetType()
  IL_000b:  ldarg.0
  IL_000c:  callvirt   instance string DuckTyping.Duck::Quack()
  IL_0011:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object,
                                                                object)
  IL_0016:  ret
} // end of method Program::doQuackStatic

Y aquí el del método dinámico, lo he colapsado porque ocupa 130 líneas, pulsa sobre el nombre para ver el código:

.method private hidebysig static void  doQuack(object duck) cil managed
{
  .param [1]
  .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       284 (0x11c)
  .maxstack  13
  .locals init ([0] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0000,
           [1] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0001,
           [2] class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo[] CS$0$0002)
  IL_0000:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site3'
  IL_0005:  brtrue.s   IL_005c
  IL_0007:  ldc.i4     0x100
  IL_000c:  ldstr      "WriteLine"
  IL_0011:  ldnull
  IL_0012:  ldtoken    DuckTyping.Program
  IL_0017:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_001c:  ldc.i4.4
  IL_001d:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_0022:  stloc.0
  IL_0023:  ldloc.0
  IL_0024:  ldc.i4.0
  IL_0025:  ldc.i4.s   33
  IL_0027:  ldnull
  IL_0028:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_002d:  stelem.ref
  IL_002e:  ldloc.0
  IL_002f:  ldc.i4.1
  IL_0030:  ldc.i4.3
  IL_0031:  ldnull
  IL_0032:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_0037:  stelem.ref
  IL_0038:  ldloc.0
  IL_0039:  ldc.i4.2
  IL_003a:  ldc.i4.0
  IL_003b:  ldnull
  IL_003c:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_0041:  stelem.ref
  IL_0042:  ldloc.0
  IL_0043:  ldc.i4.3
  IL_0044:  ldc.i4.0
  IL_0045:  ldnull
  IL_0046:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_004b:  stelem.ref
  IL_004c:  ldloc.0
  IL_004d:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                               string,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>,
                                                                                                                                                               class [mscorlib]System.Type,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
  IL_0052:  call       class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
  IL_0057:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site3'
  IL_005c:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site3'
  IL_0061:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>>::Target
  IL_0066:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site3'
  IL_006b:  ldtoken    [mscorlib]System.Console
  IL_0070:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_0075:  ldstr      "Dynamic: {0} says {1}"
  IL_007a:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site4'
  IL_007f:  brtrue.s   IL_00b3
  IL_0081:  ldc.i4.0
  IL_0082:  ldstr      "GetType"
  IL_0087:  ldnull
  IL_0088:  ldtoken    DuckTyping.Program
  IL_008d:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_0092:  ldc.i4.1
  IL_0093:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_0098:  stloc.1
  IL_0099:  ldloc.1
  IL_009a:  ldc.i4.0
  IL_009b:  ldc.i4.0
  IL_009c:  ldnull
  IL_009d:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_00a2:  stelem.ref
  IL_00a3:  ldloc.1
  IL_00a4:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                               string,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>,
                                                                                                                                                               class [mscorlib]System.Type,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
  IL_00a9:  call       class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
  IL_00ae:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site4'
  IL_00b3:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site4'
  IL_00b8:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>>::Target
  IL_00bd:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site4'
  IL_00c2:  ldarg.0
  IL_00c3:  callvirt   instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>::Invoke(!0,
                                                                                                                                                    !1)
  IL_00c8:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site5'
  IL_00cd:  brtrue.s   IL_0101
  IL_00cf:  ldc.i4.0
  IL_00d0:  ldstr      "Quack"
  IL_00d5:  ldnull
  IL_00d6:  ldtoken    DuckTyping.Program
  IL_00db:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_00e0:  ldc.i4.1
  IL_00e1:  newarr     [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
  IL_00e6:  stloc.2
  IL_00e7:  ldloc.2
  IL_00e8:  ldc.i4.0
  IL_00e9:  ldc.i4.0
  IL_00ea:  ldnull
  IL_00eb:  call       class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags,
                                                                                                                                                                             string)
  IL_00f0:  stelem.ref
  IL_00f1:  ldloc.2
  IL_00f2:  call       class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags,
                                                                                                                                                               string,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Type>,
                                                                                                                                                               class [mscorlib]System.Type,
                                                                                                                                                               class [mscorlib]System.Collections.Generic.IEnumerable`1<class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)
  IL_00f7:  call       class [System.Core]System.Runtime.CompilerServices.CallSite`1<!0> class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder)
  IL_00fc:  stsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site5'
  IL_0101:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site5'
  IL_0106:  ldfld      !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>>::Target
  IL_010b:  ldsfld     class [System.Core]System.Runtime.CompilerServices.CallSite`1<class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>> DuckTyping.Program/'<doQuack>o__SiteContainer2'::'<>p__Site5'
  IL_0110:  ldarg.0
  IL_0111:  callvirt   instance !2 class [mscorlib]System.Func`3<class [System.Core]System.Runtime.CompilerServices.CallSite,object,object>::Invoke(!0,
                                                                                                                                                    !1)
  IL_0116:  callvirt   instance void class [mscorlib]System.Action`5<class [System.Core]System.Runtime.CompilerServices.CallSite,class [mscorlib]System.Type,string,object,object>::Invoke(!0,
                                                                                                                                                                                           !1,
                                                                                                                                                                                           !2,
                                                                                                                                                                                           !3,
                                                                                                                                                                                           !4)
  IL_011b:  ret
} // end of method Program::doQuack

Sin entrar a mirar qué hace el código, podemos imaginar que entre 14 y 130 líneas de código debe haber alguna diferencia de rendimiento.

Interfaces y librerías para Duck Typing

Cuando desarrollas en unos cuantos lenguajes siempre echas de menos cosas de uno y de otro en el que estás utilizando en ese momento. Cuando paso de JavaScript a C# no suelo echar muchas cosas de menos, pero un sistema de duck-typing o structural-typing si que vendría muy bien, sobre todo cuando utilizas clases anónimas que tienes que devolver a alguien.

public interface IProcess
{
    string Name { get; set; }
    int Id { get; set; }
}
static void Main(string[] args)
{
    var procList = (from x in System.Diagnostics.Process.GetProcesses().AsQueryable()
            select new { Name = x.ProcessName, Id = x.Id }).ToList();
    printProcList(procList);
}

private static void printProcList(List<IProcess> procList)
{
    procList.ForEach((p) => Console.WriteLine("{0}: {1}", p.Id, p.Name));
}

El código anterior no funciona, porque tendremos que crear una clase que implemente el interfaz y rellenarla con los datos que nos llegan. En nuestro caso podríamos hacerlo, pero a veces no nos es posible pues usamos librerías complicadas de las que no tenemos el código o si lo tenemos tampoco le podemos meter mucha mano.

Si queremos forzar duck typing en C# contra métodos que no están pensados para duck typing inicialmente, existen algunas librerías para poder hacer esto en nuestras aplicaciones .Net, aunque pueden penalizar mucho el rendimiento. Una de ellas es impromptu que nos permite hacer cosas como usar un tipo anónimo y convertirlo a un interfaz para poder utilizarlo en la aplicación:

    using ImpromptuInterface;

    public interface ISimpleClassProps
    {
        string Prop1 { get;  }

        long Prop2 { get; }

        Guid Prop3 { get; }
    }

...

    var tAnon = new {Prop1 = "Test", Prop2 = 42L, Prop3 = Guid.NewGuid()};

    var tActsLike = tAnon.ActLike<ISimpleClassProps>();

Como ya sabréis, los interfaces explícitos nos proporcionan una forma de comprobar en tiempo de compilación que un objeto cumple con una firma en concreto, pero eso también nos limita mucho, sobre todo con el uso de clases anónimas, pues estas no pueden implementar un interfaz. Para solucionar ésto algunos lenguajes han añadido el concepto de Structural Typing, que permite el uso de interfaces implícitos, pero que el compilador podrá comprobar, al contrario que lo que ocurre con duck typing.

Al final, creo que en el propósito de aprender un nuevo lenguaje cada año, este va a ser alguno con Structural Typing… a ver si conseguimos que lo añadan a C# algún día.

Enlaces interesantes sobre duck-typing y C#:

¿Como esperar con await en una expresión lambda?

Hoy he necesitado escribir un código dentro de una lambda que llamaba a métodos asíncronos. Normalmente estamos acostumbrados a marcar métodos asíncronos con la palabra async y ya podemos usar await a diestro y siniestro, pero ¿cómo se hace con una lambda? Pues en realidad igual, marcando la lambda con async:

Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);
});

Y por supuesto, la función lambda también es awaitable:

Func<Task<string>> t = async () =>
{
    await Task.Delay(1000);
    return "Hello";
};
System.Diagnostics.Debug.WriteLine(await t());

Por cierto, id con cuidado con ésto último que he hecho, poner un await como argumento, pues puede que nos llevemos alguna sorpresa… pero ya os lo cuento en otro mini-post.

MVP Visual C# 2014

Microsoft Most Valuable Professional
2014 va a ser un año lleno de retos, algunos de ellos ya los tenía en mi lista de propósitos y despropósitos, pero el día 1 de enero recibí el correo que me anunciaba mi nombramiento como MVP de Microsoft® en el área de Visual C#. No te esperas recibir un email así, el primer día del año, durante una de esas largas comidas familiares. Casi me atraganto con los polvorones :)

Sé que algunos de vosotros os alegraréis de que “vuelva” al C#, los que leéis mi blog o habéis venido a alguna de mis últimas charlas, sabréis que llevo una temporada escribiendo más sobre JavaScript que sobre C#. Aún así en mi día a día C# sigue siendo mi lenguaje favorito y es el más utilizado en todos los proyectos que desarrollamos en el MICTT. Tengo un montón de temas sobre los que quería hablar y ahora tengo un motivo más para seguir compartiendo todo lo que estamos construyendo en el MICTT con PCL, IOC, TDD y todas las demás siglas que tanto nos gustan como KISS, DRY, YAGNI y PEBKAC.

Nos vemos muy pronto en Netsaimada. ¡Gracias!
Juanma

Consumir SOAP desde WinJS

soap
No todos los servicios proporcionan una API Restful para acceder a ella de forma sencilla desde nuestras Apps, así que es posible que tengamos que pelearnos con servicios SOAP, que son más pesados y complicados, pero a la vez nos permiten trabajar operaciones y conjuntos de datos más complejos.
Consumir SOAP desde JavaScript no es complicado, sólo que es tedioso. Hoy vamos a ver dos maneras que podremos utilizar en nuestras aplicaciones de la tienda Windows.

Descarga el código de ejemplo de Codeplex

Consumir SOAP con xhr

WinJS.xhr es un wrapper del objeto XMLHttpRequest para facilitarnos las llamadas asíncronas, así que utilizar SOAP desde xhr va a ser tan sencillo o complicado como con el XMLHttpRequest original.

Para el siguiente código vamos a utilizar uno de los ejemplos de servicios web SOAP que hay en http://www.webservicex.net/, en concreto el servicio ChangeLengthUnit que realiza conversión de unidades.

En una aplicación de la tienda Windows creamos una página y en el tag body ponemos un cuadro de texto, un botón y un div como estos:

Introduce los centímetros: <input type="number" id="mmValue" value="0.0"/>
<button id="xhrTest">Conexión con WinJS.xhr</button>
<div id="result"></div>

En default.js, añadimos el código que sigue tras la línea donde se llama a WinJS.UI.ProcessAll(), creando el código XML que enviaremos al servicio SOAP para llamar a la acción SOAP ChangeLengthUnit. Podéis ver un ejemplo en la página de descripción de la operación, necesita tres parámetros: el valor numérico, las unidades origen y las unidades destino. Para el ejemplo convertiremos de centímetros a pulgadas:

var data = '<?xml version="1.0" encoding="utf-8"?>' +
            '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + 
            ' xmlns:xsd="http://www.w3.org/2001/XMLSchema" ' +
            ' xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' +
            '  <soap:Body>' +
            '    <ChangeLengthUnit xmlns="http://www.webserviceX.NET/">' +
            '      <LengthValue>'+mmValue.value+'</LengthValue>' +
            '      <fromLengthUnit>Centimeters</fromLengthUnit>' +
            '      <toLengthUnit>Inches</toLengthUnit>' +
            '    </ChangeLengthUnit>' +
            '  </soap:Body>' +
            '</soap:Envelope>';

var options = {
    url: "http://www.webservicex.net/length.asmx",
    type: "post",
    headers: {
        "Content-Type": "text/xml; charset=utf-8",
        "SOAPAction": "http://www.webserviceX.NET/ChangeLengthUnit"
    },
    data: data
};

Fijaos que he configurado la llamada xhr para llamar por POST y he incluido dos cabeceras específicas para la llamada, el Content-Type, que debe ser xml y la SOAPAction, que debe indicar a qué servicio estamos llamando. Ahora llamamos a la función xhr con las opciones que hemos configurado y esperamos la respuesta, que vuelve a ser un XML del que podremos extraer la información.

WinJS.xhr(options)
.done(
    function (request) {
        var doc = request.responseXML.documentElement;
        var output = doc.getElementsByTagName("ChangeLengthUnitResult");

        result.innerHTML = "La longitud es "+ window.toStaticHTML(output[0].textContent)+" pulgadas";
        result.style.backgroundColor = "#00A000";
    },
    function (error) {
        result.innerHTML = "Status: " + error.status + " : " + error.statusText;
        result.style.backgroundColor = "#FF0000";
    },
    function (progress) {
        result.innerText = "Ready state is " + progress.readyState;
        result.style.backgroundColor = "#0000A0";
    });

Todo esto para un servicio web bastante sencillo. Si empezamos a añadir tipos de datos complejos la cosa se complica.
Como estamos en Windows 8 / 8.1, tenemos una gran ventaja, podemos realizar la conexión con el servicio web mediante C#, que es capaz de leer la descripción WSDL del servicio y generar un proxy con las clases tipificadas, para luego utilizar esa clase desde JavaScript a través de las proyecciones WinMD.

Puente SOAP con C#

Como ya comenté en un artículo anterior, podemos utilizar librerías escritas en C# o C++ desde nuestros proyectos JavaScript, sólo tenemos que crear una librería tipo Windows Runtime Component y respetar unas normas básicas. Una vez creada la librería y referenciada desde nuestro proyecto JavaScript, utilizar servicios SOAP es muy fácil, basta con pulsar botón derecho sobre el proyecto C# y añadir una referencia al servicio. El Visual Studio nos creará las clases y llamadas que necesitamos para comunicarnos con el mismo.

Add WebService

Con la referencia al servicio ya tenemos todo lo necesario, sólo nos falta crear un método dentro de una clase C# para publicarlo y que se pueda utilizar desde JavaScript. Como las llamadas al servicio son asíncronas creamos un método que devuelve un tipo IAsyncOperation; podemos convertir la Task del cliente SOAP directamente a este interfaz con el método de extension AsAsyncOperation:

public sealed class LengthUnits
{
    LengthUnitService.lengthUnitSoapClient client;
    public LengthUnits()
    {
        client = new LengthUnitService.lengthUnitSoapClient();
    }
    public IAsyncOperation<double> GetAsync(double number,
        LengthUnitService.Lengths from, LengthUnitService.Lengths to)
    {
        return client.ChangeLengthUnitAsync(number, from, to).AsAsyncOperation();
    }
}

Et voilà, ya lo podemos utilizar desde JavaScript sin ensuciarnos las manos parseando XML:

csTest.onclick = function (e) {
    var cl = new SoapClient.LengthUnits();
    cl.getAsync(mmValue.value, SoapClient.LengthUnitService.Lengths.millimeters, SoapClient.LengthUnitService.Lengths.inches).done(
        function (c) {
            result.innerHTML = "La longitud es " + c + " pulgadas";
            result.style.backgroundColor = "#00a000";
        },
        function (err) {
            result.innerHTML = window.toStaticHTML(err);
            result.style.backgroundColor = "#FF0000";
        },
        function (progress) {
            result.innerHTML = window.toStaticHTML("percent: " + progress + "%");
            result.style.backgroundColor = "#0000a0";
        }
        )
}

Progreso

Un momento, ¿ahora no está llegando nunca a la función de progreso? Pues no, porque para tener progreso necesitamos utilizar el interfaz IAsyncOperationWithProgress. Nos llevará un poco más de trabajo, la clase AsyncInfo y las palabras async y await nos lo van a poner fácil:

    public Windows.Foundation.IAsyncOperationWithProgress<double, string> GetWithProgressAsync(double number,
        ServiceReference1.Lengths from, ServiceReference1.Lengths to)
    {
        return AsyncInfo.Run(async delegate(CancellationToken cancellationToken,
            IProgress<string> progress)
        {
            double result;
            progress.Report(client.State.ToString());
            await client.OpenAsync();
            progress.Report(client.State.ToString());
            result = await client.ChangeLengthUnitAsync(number, from, to);
            progress.Report(client.State.ToString());
            await client.CloseAsync();
            progress.Report(client.State.ToString());
            return result;
        });
    }

Seguro que hay alguna manera más, cuando la encuentre os la contaré!

Descarga el código de ejemplo de Codeplex

Mejorar el rendimiento del canvas HTML5 desde C#

IMG_4102
Desarrollar aplicaciones para la Tienda Windows en JavaScript tiene sus ventajas e inconvenientes. Aunque en la mayoría de los casos no lo vamos a notar, cuando tratamos grandes cantidades de datos y tenemos que realizar modificaciones sobre estas, lo más probable es que se penalice el rendimiento.

Estoy desarrollando la nueva versión de Fingerpaint y quiero añadir una herramienta de relleno de color.
No es complicado de hacer, hay bastantes algoritmos bien optimizados y el context tiene los métodos getImageData y putImageData que nos dan el array de datos para modificar el contenido del canvas a nivel de pixel:

width = context.canvas.width;
height = context.canvas.height;
imageData = context.getImageData(0, 0, width, height);
data = imageData.data;
for(var i=0;i<data.length;i++){
  //tratar los píxeles de data uno por uno
}

El problema de hacer esto en JavaScript es que el lenguaje no está optimizado para este tipo de operaciones e intentar hacer un relleno un poco inteligente (estilo cubo de pintura) va a tardar mucho más de lo que estamos acostumbrados con cualquier aplicación de dibujo moderna.

Componentes Windows Runtime (WinRT)

Por suerte, mi aplicación FingerPaint es para Windows 8 (en realidad 8.1) y puedo usar librerías desarrolladas en otros lenguajes, como C# o C++, que me permitirán realizar estas operaciones mucho más rápido.
Es muy fácil tener código en otros lenguajes dentro de una aplicación JavaScript de la tienda Windows, sólo tenemos que añadir una nueva librería de tipo Windows Runtime Component en el lenguaje que prefiramos. Con este tipo de proyectos estamos indicando que es una librería que puede utilizarse desde cualquier otro lenguaje.

Lo que estamos haciendo es crear una librería con WinMD, el mismo sistema de metadatos que las librerías de sistema en Windows Runtime. En esta imagen creamos un nuevo componente en C#:
Windows Runtime Component

A continuación tenéis el ejemplo de código. Está dividido en dos métodos:

public sealed class Fill
{
   private byte[] _data;

   public void BeginFill(int x, int y, int width, int height, 
       [ReadOnlyArray] byte[] bitmap, string hexColor, byte tolerance)
   {
     _data=bitmap;
     //
     //aquí irá mi código de flood fill que modificará los datos
     //dentro de _data, pero al ser ReadOnly no volverán a JavaScript
     //modificados.
     //
   }

   public void EndFill([WriteOnlyArray] byte[] target)
   {
     _data.CopyTo(target,0);
   }
}

Si os fijáis, la clase es sealed y los parámetros array están decorados con los atributos Read/WriteOnlyArray. Estas dos características son requisito indispensable para que nuestra clase y métodos se puedan utilizar desde cualquier otro lenguaje.

Añadiremos el proyecto que hemos creado en las referencias de nuestra aplicación JavaScript y desde el código JavaScript las llamadas se verán así, como si fueran llamadas a cualquier otra librería JavaScript, pero tú y yo sabemos que está hecho en C#:

width = context.canvas.width;
height = context.canvas.height;
imageData = context.getImageData(0, 0, width, height);
data = imageData.data;
//creamos un objeto Fill
var helper=new ImageHelpers.Fill();
//calculamos el fill
helper.beginFill(x,y,width,height,data,"#00ff00",50);
//escribimos el resultado en imageData
helper.endFill(data);
//escribimos el resultado en el canvas
context.putImageData(imageData, 0, 0);

Para poder modificar el canvas necesitamos que nuestro método externo modifique directamente el array obtenido con getImageData, pero cuando trabajamos con componentes de WinRT y queremos pasar un array sólo podemos hacerlo en uno de estos dos modos:

  • ReadOnlyArray: trabajamos sobre una copia del array y cualquier modificación no se traspasará entre los contextos, ni siquiera si devolvemos el array como valor de retorno
  • WriteOnlyArray: podemos escribir sobre el array original… pero este siempre viene vacío, no veremos la información original

Adicionalmente, canvas no nos permite introducir un array de datos dentro de imageData que no haya sido creado por el propio canvas, así que si utilizáramos un parámetro de retorno tendríamos que copiar uno por uno los bytes del array, con lo que perderíamos el rendimiento que ganamos por hacerlo en C#.
Entonces, y por eso el artículo de hoy, tenemos que realizar la operación en dos pasos: en el primero obtenemos los datos originales y en el segundo escribimos sobre la memoria del canvas desde C#. Estas operaciones son bastante rápidas y casi no penalizan el rendimiento (entre 1 y 3 milisegundos en un i5), así que nos queda bastante margen para modificar el contenido del canvas y mejorar considerablemente la velocidad del fill con nuestro método C#.

Depuración híbrida

Para poder depurar nuestro código C# dentro de una aplicación JavaScript tendremos que indicar en las propiedades del proyecto qué tipo de lenguaje queremos depurar, a alguien se le olvidó poner Managed+Script así que por ahora sólo podremos depurar en uno o en otro, si hubiéramos elegido C++ como lenguaje no tendríamos este problema, pero tendríamos otros :P.
Script or Managed debug

Happy finger painting!