Repetid conmigo mil veces:

Los elementos del DataTemplate en un FrameworkElement con DataBinding deben ser propiedades.

 

Los elementos del DataTemplate en un FrameworkElement con DataBinding deben ser propiedades.

Los elementos del DataTemplate en un FrameworkElement con DataBinding deben ser propiedades.

Los elementos del DataTemplate en un FrameworkElement con DataBinding deben ser propiedades.

Los elementos del DataTemplate en un FrameworkElement con DataBinding deben ser propiedades.

O en otras palabra, cuando uses el modelo MVVM (al menos en Windows Phone) los elementos de binding en la plantilla de datos deben ser propiedades aunque sean del tipo genérico.
Es decir en
            <ListBox>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin=»10″>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <TextBlock Text=»{Binding Name}» Grid.Row=»0″ FontWeight=»Bold» FontSize=»20″/>
                            <TextBlock Text=»{Binding Surname}» Grid.Row=»1″ FontSize=»10″/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
tanto Name como Number deben ser propiedades y públicas.
Me da igual si está documentado en algún lado o no, pero me ha costado sudores darme cuenta. El objeto que se va a enlazar en la colección puede ser una clase o una estructura, pero debe ser pública sí o sí.
Ya os imagináis por qué estoy poniendo esta entrada, ¿no? 😛

 

Yoda Code y el Optimizador de .NET

Dicen que a veces la vida te da sorpresas, sorpresas te da la vida. Y en este caso es completamente cierto. A raíz de la entrada anterior, un par de amigos comentaron en Twitter sobre la misma, haciendo uno de ellos hincapié en el Yoda Code que había puesto.

No soy yo muy partidario de Yoda, más que nada porque todos los lenguajes con los que programo suelen avisarte del uso de un solo igual en las comparaciones, aparte de que, como perteneciente a la vieja escuela, veo raro poner eso al revés. No es que sea gratuito, sino que viene de la forma en que aprendimos los lenguajes.

Digamos que para nosotros, en null==handler, null es un lvalue y debe ir a la derecha sí o sí. A ver, que es un poco tontería porque es una comparación, no una asignación, pero así lo veo al menos yo.

Evidentemente no hay nada en contra de utilizar al amigo Yoda (esperemos que Spielberg no se nos enfade y empiece a pedirnos derecho de marca). Simplemente yo lo veo extraño.

***

Pues nada, el RFOG pone las manos en la masa y decide investigar si hay alguna diferencia en el código generado por el compilador entre ponerlo de una forma u otra. Y ahí es donde se lleva una sorpresa que seguro vosotros tampoco os esperáis (no, no es la que pensáis. Paciencia, joven padawan).

Para ello se crea un proyecto de consola con el siguiente código fuente en C#:

        static void Main(string[] args)
        {
            var a = 33;
            if (40 == a)
                Console.WriteLine("Es diferente");
            else
                Console.WriteLine("Es igual");
        
            if (a == 40)
                Console.WriteLine("Es diferente");
            else
                Console.WriteLine("Es igual");
        }

Y en C++ (más bien C, pero a efectos de nuestro interés da igual):

            auto a = 33;
            if (40 == a)
                printf("Es diferente");
            else
                printf("Es igual");
        
            if (a == 40)
                printf("Es diferente");
            else
                printf("Es igual");
			return 0;

Y finalmente en C++/CLI, más que nada por mor de completitud:

int main(array<System::String ^> ^args)
{
	auto a = 33;
	if (40 == a)
		Console::WriteLine("Es diferente");
	else
		Console::WriteLine("Es igual");

	if (a == 40)
		Console::WriteLine("Es diferente");
	else
		Console::WriteLine("Es igual");
	return 0;
}

Si os fijáis, el código es idéntico excepto sus diferencias de sintaxis. Para ver el código resultante, se pone un punto de interrupción en la asignación de la variable a, se lanza el programa y luego nos vamos a Debug -> Windows -> Disassembly (o pulsamos Alt-8). Y miramos el código resultante en los tres casos. Primero en Debug y para C#:

clip_image002

Nítido como el agua: tanto monta, monta tanto, a==40 que 40==a. Perfecto. Vamos con C++/CLI:

clip_image004

Más o menos como en C#, de hecho el código es prácticamente el mismo. Finalmente C++ nativo:

clip_image006

Otra vez como lo esperado. Ninguna diferencia en la generación de código en la parte de la comparación. Bueno, no, algo peor porque el código generado en C++ es a todas luces peor que el de C#, cosa que me extraña. Esta es la primera sorpresa, pero todavía hay más.

De todos modos, si te preocupaba la generación de código tanto usando a Yoda como sin usarlo, ves que no hay diferencia.

Aun así, te recomiendo que sigas leyendo. Lo interesante viene ahora.

***

Pasemos a las versiones Release, que se suponen generan mejor código. La versión en C# genera exactamente el mismo código, por lo que realmente el compilador no está haciendo nada de nada. No hace falta que ponga ninguna captura: el código es idéntico.

En C++/CLI ya vemos algo diferente, y es que el breakpoint se ha movido él solo hacia abajo. Eso quiere decir que ha entrado el optimizador de código:

clip_image008

La versión en ensamblador es terroríficamente simple:

clip_image010

Como el compilador ya sabe el resultado, simplemente ejecuta el código adecuado y punto. El que el primer bloque de ensamblador sea tan largo se debe al prefijo de carga de main, y ese je 00000015 no es más que la comparación de a con 40, que el optimizador todavía realiza.

¿Por qué lo hace? Buena pregunta: otro hilo podría haber cambiado el valor de a, por lo tanto es necesaria la comparación. Ya sé que en este contexto no es válido, pero en otro sí que podría serlo.

Lo mismo nos pasa con C++ nativo, y ahora sí que vemos cómo el código está más que optimizado y apenas son unas cuantas línea de código ensamblador. La siguiente captura está dedicada a todos aquellos que piensan que no vale la pena desarrollar en C++:

clip_image012

***

Hasta ahora está todo meridianamente claro: No hay diferencia entre ir con Yoda o sin él y que el compilador de C++ es una caña generando código.

¿Estamos todos de acuerdo? Yo al menos lo estaba hasta hace bien poco. Ahora os dedico dos nuevas capturas de pantalla:

clip_image014

clip_image016

Y este es el bombazo: el código en .NET es casi idéntico al nativo. Cosa que hace unos años no pasaba. ¿Cómo he obtenido esas capturas? Os vais a Tools -> Options -> Debugging -> General -> Supress JIT optimization on module load y desmarcáis la casilla.

Bueno, el código es casi tan bueno como el nativo, pero fijaos que es mucho menor que las versiones anteriores. Esto me lleva a confirmar una sospecha: la gente de Microsoft está haciendo bien sus deberes, llevando al compilador de C# a cotas cada vez más altas.

Y es que con los años parece ser que han mejorado mucho:

Cross Threading en MVVM y Windows Phone 8 (y también 7)

 

Os cuento una rápida que seguro os va a solucionar un montón de quebraderos de cabeza y que me ha traído loco un rato hasta que me he dado cuenta del pufo.

Todo viene cuando estás usando el patrón MVVM y varías algún dato que se está mostrando en algún componente visual. Todos los tutoriales te dicen que heredes de ObservableCollection y que implementes el método NotifyPropertyChanged(). Y te ponen el típico ejemplo:

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
    var handler = PropertyChanged;
    if (null != handler)
    {
      handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Hasta aquí nada extraño. Cuando una propiedad cambia su valor, quien quiera que esté observando tu colección se enterará del mismo y actuará en consecuencia. Eso si alguien está observando tu colección, que no siempre ocurre, sobre todo si hay algún converter de por medio (ya hablaré largo y tendido sobre esto).

Pero el problema viene cuando ese cambio se produce fuera del hilo principal de la aplicación, como suele pasar a poco que estés haciendo algo medianamente serio. No veo yo bloqueando tu aplicación mientras un servicio web o lo que sea se baja lo que se tenga que bajar, aparte de que en Windows Phone la mayoría de llamadas a todo eso son asíncronas.

Si haces un cambio en un hilo que no sea el de la UI, en Windows Phone 7.5 no pasa nada. Simplemente se ejecuta el código y en general tu colección cambia de valor y a veces, sólo a veces, la visualización cambiará. O no.

En Windows Phone 8 ocurre otra cosa no muy diferente. Recibes dos first chance de dos excepciones diferentes, el IDE no te captura nada a no ser que cambies la configuración de captura de excepciones y el hilo en el que se esté ejecutando el código se muere silenciosamente. Y por supuesto no se cambia nada en la parte visual de tu aplicación.

Vale. Tonto si no pones un bloque try/catch en cada hilo. Pero aquí estamos hablando de otro combate.

¿Cómo puñetas puedo actualizar eso de forma automática? Es decir, ¿tengo que hacer un BeginInvoke() cada vez que asigne algo? La respuesta corta es que sí. La larga es lo siguiente.

Primero tienes que crearte una propiedad nueva en la clase App que vas a llamar UiDispatcher (o como te salga de los OO):

internal static Dispatcher UiDispatcher=Current.RootVisual.Dispatcher;

Y luego cambia el código de arriba por el siguiente:

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var handler = PropertyChanged;
            if (null != handler)
            {
                if (App.UiDispatcher.CheckAccess())
                    handler(this, new PropertyChangedEventArgs(propertyName));
                else
                    App.UiDispatcher.BeginInvoke(() => handler(this, new PropertyChangedEventArgs(propertyName)));
            }
        }  

Ahora, si os fijáis, si el cambio de vuestra propiedad se realiza dentro del hilo principal, se llamará directamente, y si lo hace desde otro lo hará dentro del Dispatcher de la UI.

¿Por qué una propiedad y no llamar siempre a Current.RootVisual.Dispatcher? Pues porque podría interesarnos tener diferentes dispatchers para otras cosas, pero esa es otra guerra que hay que combatir en otro momento.