Categoría: Herramientas

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!

Anuncios

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#:

Desarrollar desde una Surface RT

Surface RT on Boeing 733-800 (Virgin Australia)

En Windows RT, aparte de las aplicaciones de la suite Office, sólo tenemos aplicaciones de la tienda Windows, ninguna de nuestras aplicaciones favoritas de desarrollo funciona en el escritorio de Windows RT.  No tenemos Visual Studio, que por otra parte consumiría más recursos de los que disponemos en una Surface RT.

A pesar de esto, no todo está perdido, existen herramientas que podremos utilizar para desarrollar desde la tableta.

Windows RT en el mundo real

Hace unas semanas me inscribí a un curso online de Python para aprender otro lenguaje más y de paso entender mejor un libro sobre Machine Learning que compré hace poco. Coincidió que tuvimos que pasar unos días en el hospital para ver cómo nacía una nueva versión de mi chica y yo, pero eso es otra historia.

El caso es que como íbamos a pasar unas cuantas noches en vela, me llevé la Surface RT para entretenerme entre cambio de paquetes. Llevé la RT y no el portátil por varios motivos, entre ellos el peso, volumen y duración de batería… pero tenía un problema, no podía instalar el entorno de Python.

¿Qué podemos hacer si sentimos la llamada del código y sólo tenemos una tableta?

Podemos buscar por la web, pues hay bastantes páginas donde podemos desarrollar directamente desde la página para diferentes lenguajes, como por ejemplo jsfiddle para JavaScript, o Compile Online para casi todos, pero esa opción no me acaba de satisfacer en un “hotel” sin wifi y tirando de 3G.

Otra opción es montar una máquina virtual con el entorno y subir el VHD a Windows Azure para usarla por escritorio remoto. Es algo habitual, conozco mucha gente que utiliza este método desde su tableta y les funciona bien, pero sigo con el problema de la wifi.

Si tienes una tableta con Windows 8 RT estás de suerte. Buscando en la tienda encontré algunas aplicaciones interesantes y gratuitas, que harán nuestra vida de coders algo más llevadera cuando no tengamos un PC a mano.

Algunas aplicaciones de la Tienda Windows

Python 3 for Metro

Python 3 for Metro

Un interprete de Python 3 gratuito. Es bastante básico, pero como ahora mismo estoy aprendiendo me basta. Tiene una pantalla interactiva para poder ver los resultados de lo que estás haciendo y puede ejecutar ficheros externos. Le faltan bastantes cosas como poder cargar módulos y algo de estabilidad, pero me he llevado una grata sorpresa al encontrarlo y funciona en tabletas ARM.

Python Metro

Code Writer

Code Writer

Un editor de código estupendo con syntax highlighting para un montón de lenguajes:

Code Writer Main Window

El editor de código es muy profesional. Está muy bien resuelto para editar código desde una tableta, puedes tener abiertos múltiples ficheros en diferentes lenguajes,  colorea las palabras clave, etiquetas, cadenas, etc.

Code Writer Code Window

Con estas aplicaciones y gracias a la integración con Skydrive he podido seguir el curso y realizar los ejercicios sin problema desde una tableta.

Está claro que no es un entorno ideal, nos faltarán muchas cosas a las que nos hemos acostumbrado como intellisense y control de código, pero eh! que es una tableta! Y estoy seguro que poco a poco irán surgiendo más apps interesantes como estas.

Creación de nuestros propios snippets en dos o tres clicks

Los snippets de Visual Studio son una herramienta estupenda que nos ahorra muchísimo tiempo a la hora de escribir código. Los snippets que vienen predefinidos en VS.Net nos permiten escribir mucho código escribiendo las dos primeras letras y a golpe de tecla Tab crear propiedades, bucles y un sinfín de llamadas.

Lo que pasa con este tipo de herramientas es que tarde o temprano necesitas más, por eso te puedes crear tus propios snippets con un poco de XML, pero lo que quiero yo es que sea rápido de hacer, para esto tenemos una extensión de Visual Studio llamada Snippet Designer. Os cuento en 2 pasos cómo instalarla y en 4 más cómo crear y utilizar el snippet, no más de 5 minutos para ahorrarnos tecleos y ahorrarle un montón de dinero a nuestro jefe en horas de picar código 🙂

  1. Lo primero, instalar el Snippet Designer. Abrimos Tools>Extensions and Updates:

    createSnippet01

    Y buscamos online el Snippet Designer

    createSnippet02

  2. Una vez instalado ya podemos seleccionar el trozo de código del que queremos crear el snippet y seleccionar la opción Export as Snippet:

    createSnippet03

  3. Entonces se abrirá un editor que nos permitirá definir el snippet de forma visual: qué claves será posible cambiar desde el snippet (botón derecho>Make Replacement), cómo se llamará ese elemento, qué valor tiene por defecto, de que tipo es, etc…

    createSnippet05

    Y una ventana de propiedades donde podremos definir las características del snippet, una muy útil es el Shortcut:

    createSnippet07

  4. Una vez lo tenemos guardado (se guarda en My Snippets), reniciamos el VS.Net y ya podemos utilizar nuestro nuevo snippet, nos bastará escribir el shortcut:

    createSnippet08

    Y pulsar el tabulador para que el snippet se expanda y nos permita escribir en él:

    createSnippet09

Que lo disfrutéis!

Versión de evaluación de Windows 8 para desarrolladores

Windows 8 Hi

Ahora que el Windows 8 está recién salido del horno y disponible para desarrolladores con una suscripción MSDN o TechNet, empezarán a surgir muchísimas aplicaciones en el Windows Store.

También están listas las herramientas para la versión definitiva; son las que tendremos que usar para poder publicar una aplicación en el Windows Store y son gratuitas.

Los que no dispongáis de una suscripción MSDN y no podáis esperar a comprar la actualización, podéis descargar una versión de evaluación de 90 días.

Ah, y para el desarrollo web tenemos Visual Studio 2012 Express for the Web, también gratuito.

Participa en la traducción de Windows 8

Language Day May 14 2010Windows 8 viene con muchas novedades para las que se necesitan gran cantidad de términos nuevos. Estas características ya tienen su descripción en inglés y ahora hace falta su traducción al resto de idiomas.

Si queréis poner vuestro grano de arena, Microsoft ha habilitado una plataforma dentro del sitio Microsoft Language Portal. Podéis acceder a través del siguiente enlace, sólo hace falta un LiveID para poder participar:
http://www.microsoft.com/Language/mtcf/mtcf_home.aspx?langid=2239&cult=es-ES&WT.mc_id=dpx

Microsoft Language Portal es el lugar donde encontramos las traducciones que se han utilizado para el software, manuales y otros muchos documentos. Es una herramienta imprescindible si te dedicas a escribir artículos técnicos en lengua no inglesa.

Novedades en el SDK 7.1 de Windows Phone

Hace muy poco se ha liberado la beta del SDK 7.1 de Windows Phone y trae algunas de las novedades que ya os he comentado en este blog. Mientras esperamos impacientes que se actualice nuestro dispositivo ya podemos probar las novedades en el emulador, que además trae algunas cosas nuevas a nivel de emulación.

Para ello podemos ir descargando los ejemplos que encontraremos en el MSDN, el mejor punto de partida para probar las nuevas funcionalidades.

Una de las novedades que encontraremos nada más abrir el visual studio es que tenemos unos cuantos tipos de proyecto nuevos:

Ahora ya tenemos aplicaciones 3D en Silverlight y tres agentes, para aplicaciones que funcionarán en segundo plano gracias a la nueva característica multitarea.

Si no elegimos una de las nuevas plantillas, que sólo están disponibles para 7.1, podremos elegir para qué plataforma queremos realizar la aplicación:

De esta manera podremos empezar a probar las nuevas funcionalidades sin dejar de lado los desarrollos que ya tengamos en marcha y no puedan esperar a que la actualización llegue al teléfono.

Otra gran novedad que será muy agradecida, especialmente por aquellos desarrolladores que aún no tienen un dispositivo de pruebas, es la nueva ventana de simulación de acelerómetro …

desde la cual podemos mover el teléfono en los ejes XYZ libremente o usar gestos grabados, por ahora viene con el gesto de sacudida (shake). Desde la misma pantalla tenemos la simulación de GPS, sobre la que podemos definir diferentes puntos sobre un mapa para que se vaya moviendo:

Y para acabar IE9 en el dispositivo:

Fijaros que la barra de direcciones está ahora en la barra inferior, dando algo más de espacio a la ventana del IE9… y que pese a la polémica/susto en torno al número del SDK, la versión del sistema operativo es efectivamente 7.5 😀

Estas son algunas de las novedades más visibles, poco a poco iremos desgranando todo lo que viene como las aplicaciones en background, Live Tiles, realidad aumentada, sockets y mucho más.

Actualización de las herramientas de desarrollo para Windows Phone 7

Ya están disponibles para descarga las nuevas herramientas de desarrollo para el Windows Phone 7 (descargar). Se incluyen las siguientes mejoras:

  • Copy & Paste: ya podemos probar la nueva funcionalidad en el emulador (How to)
  • Capability Detection Tool: útil para rellenar el manifest (How to)
  • Connect Tool: para poder depurar sin tener Zune abierto, lo que nos permitirá poder usar audio y vídeo (How to)
  • Bing Maps Silverlight Control: actualizado con mejoras en gestos.
  • Xap>64MB: ahora ya se pueden mandar al dispositivo físico y depurar aplicaciones de más de 64MB.

Leed bien las instrucciones pues son dos paquetes y hace falta tener las herramientas anteriores instaladas (en inglés).

Tal como indica Duefectu por ahora las herramientas están sólo disponibles en inglés. Si tenéis instalado el entorno en castellano, o cualquier otro idioma, habrá que esperar un poco.

Enterprise Library 5.0 publicada

Microsoft patterns & practices ha publicado la nueva Enterprise Library 5.0, una colección de módulos que se pueden usar a modo de bloques prefabricados y configurables para construir grandes aplicaciones.

Como desde su primera versión, incluyen el código fuente como ejemplo de buenas prácticas y también para posibilitar modificaciones que no sean posibles a través de su extenso sistema de configuración.
Al ser un sistema modular no hace falta distribuir toda la librería entera con nuestra aplicación, sino que usaremos sólo los bloques que nos convengan. Para los que aún no sepáis qué es, la colección entera incluye:
  • Caching Application Block: para almacenar datos temporalmente, tanto en memoria como en un almacen de datos intermedio.
  • Cryptography Application Block: para incorporar fácilmente hashing y encriptación simétrica a nuestras aplicaciones.
  • Data Access Application Block: simplifica el acceso a datos, normalmente basado en ADO.Net y DataSets, independizando la capa de acceso a datos del motor.
  • Exception Handling Application Block: para definir estrategias comunes de manejo de excepciones y errores a todos los niveles de las capas de la aplicación
  • Logging Application Block: los desarrolladores pueden incluir el registro de mensajes sobre un amplio abanico de fuentes de manera simple y sencilla.
  • Policy Injection Application Block: usando los mecanismos de inyección de Unity se pueden implementar políticas de captura de mensajes para canalizar la implementación de partes comunes como el manejo de excepciones o el caching.
  • Security Application Block: para incorporar autorización y funcionalidades de caché de seguridad en las aplicaciones.
  • Unity Application Block: un contenedor de inyección de dependencias, para que podamos crear aplicaciones fácilmente extensibles
  • Validation Application Block: permite crear reglas de validación para objetos que puedan ser usadas en otras capas de la aplicación.

Las verdaderas razones detrás de un cambio

Hoy, leyendo una entrada de Jose Fco Bonnin, me han empezado a sonar las alarmas y he sentido la necesidad de investigar un poco.

El motivo de su post es que en el VS2008 se quitaron algunas reglas del FxCop, entre ellas la “CA1818 – Do not concatenate strings inside loops“.
Tal como Jose ha comprobado, no ha habido ningún cambio en el framework para que esa regla se pueda quitar y el motivo que alega el equipo de Code Analysis es que la han quitado debido a “high noise or no longer applicable analysis“.
Cualquier desarrollador de .Net que se haya preocupado un mínimo por el rendimiento una de las primeras cosas que suele mirar es si la aplicación está generando demasiados strings; así que debe haber alguna otra razón para quitar esa regla del motor.
Tras buscar un poco he encontrado unas entradas que nos dan alguna una pista: tanto en una conversación en el foro de fxcop beta como en una nota de su blog comentan que el “data flow analysis engine”, el encargado de comprobar esa regla, ha sido eliminado porque no funcionaba bien, era muy lento y además indeterminista.
Es una opinión/deducción personal, pero creo que nos han contado una verdad a medias y el impacto de haber quitado esa regla puede ser verdaderamente alto en aplicaciones grandes.
Teóricamente Phoenix iba a arreglar el desaguisado pero no parece que tenga continuidad, así que habrá que ver si en vs2010, que vuelve a tener un data flow engine, las reglas se han vuelto a activar. ¿Alguien lo ha probado ya?