tag:blogger.com,1999:blog-224714462024-03-07T08:36:38.942+01:00Comma NETIan Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.comBlogger156125tag:blogger.com,1999:blog-22471446.post-54506276270038884192010-10-26T00:41:00.003+02:002010-10-26T00:48:21.128+02:00La Cara Oculta de Delphi 6<div><span class="first">Todo hombre de</span> negocios sabe que no debe regalar aquello que puede vender. Todo hombre de negocios inteligente sabe, además, que no debe tirar aquello que puede regalar.</div><div>"La Cara Oculta de Delphi 6" es un libro al que profeso mucho cariño. Invertí en su preparación muchas horas, me sirvió para afianzar mi empresa... y estoy satisfecho con el resultado. Pero ya ha pasado tiempo suficiente como para dar por cerrada una etapa profesional, y para pasar a la siguiente. Aquí está la versión electrónica del libro, por si a alguien todavía le interesa:</div><ul type="square"><li><a href="http://www.marteens.com/pdfs/TheDarkSideOfDelphi6.pdf" target="_blank" title="PDF (7MB)">La Cara Oculta de Delphi 6</a></li></ul><div>Es tiempo de ascender otro escalón...</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com36tag:blogger.com,1999:blog-22471446.post-73475469498608647042010-09-08T00:18:00.020+02:002010-09-08T02:47:48.029+02:00Cómo espiar los mensajes de un control<div><img style="float:right; margin:1px 0 1px 8px; border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDW8NFPyLlnVaPbsHM3IqpEnOJ4ZBr4rsn38k4UwN9IqviIYhaHQVjIjdmkSF0MClJKEmQ64ZFDu92FBHss2GI2Lx_kDqDQuvuT6P3nc8_DhjbRaFNwU7vRDsWUmCEhnE7Ran6/s320/spyingmsgs.jpg" border="0" alt="Esto es lo que se ve al espiar ciertas ventanas" /><span class="first">Sigo con trucos necesarios para</span> implementar un sistema de gestión de ventanas más o menos complejo. Imagine que tiene un botón, y que debe responder a los clics del mismo. Ni lo piense: desde la ventana, o desde la clase que contiene al botón, añade un manejador al evento <i>Click</i> del botón. Esta es una ventaja que tiene .NET y su sistema de eventos <i>multicast</i> sobre el viejo y más sencillo sistema de eventos de Delphi nativo. En Delphi nativo solamente podíamos asociar un único manejador a cada evento. En .NET, el evento puede disparar cuantos manejadores necesitemos.</div><div>La gran pregunta: ¿y qué ocurre si lo que quiero interceptar es un mensaje que no tiene un evento de "alto nivel" asociado? La solución "clásica" es crear una clase derivada a partir del botón. Pero, ¿y si no es posible hacerlo? Por ejemplo, puede que le toque lidiar con un botón que usted <i>no ha creado</i> (la culpa siempre es de otro). En mi caso, no se trata de un mero botón, sino de la mismísima ventana principal. Estoy escribiendo un gestor genérico de ventanas, y necesito interceptar el mensaje <i>WM_ACTIVATEAPP</i>. Podría obligar al programador que utilice mi gestor a derivar <i>su</i> ventana principal de una clase base <i>mía</i>, debidamente retocada. Sería chapucero, no obstante.</div><div style="font-size: 90%; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">Este es un problema bastante general del modelo de programación orientada a objetos que terminó imponiéndose en nuestros lenguajes de programación: el tipo de objeto va unido indisolublemente a su entidad. Un coche de turismo no se puede convertir dinámicamente en una ambulancia... aunque en la vida real, los turismos puedan utilizarse así. Este problema hace complicado trabajar con bases de datos orientadas a objetos cuyo modelo de objetos sea parecido al de C++, Java o C#. No me malinterprete: el modelo con tipos estáticos es estupendo para escribir cierto tipo de programas, pero es malo para las bases de datos, en las que un objeto de la clase Contacto puede fácilmente en un Cliente, y no podemos perder las referencias al contacto durante la metamorfosis.</div><div>Lo interesante es que existe una solución muy conocida para este problema... en el API de bajo nivel de Windows; si busca <i>window subclassing</i> encontrará montones de páginas sobre esta técnica. La pregunta es, ¿cómo conseguir lo mismo utilizando Windows Forms?</div><div>La respuesta la tiene la clase <i>NativeWindow</i>, que nos evita tener que usar PInvoke o cosas peores. ¿Hay que interceptar los mensajes que recibe un control? Pues creamos una clase derivada de <i>NativeWindow</i>, sobrescribimos su método virtual <i>WndProc</i> y, para asociarla al puñetero botón, usamos el método <i>AssignHandle</i> de <i>NativeWindow</i> para copiar en una instancia de la nueva clase el identificador de ventana del botón a vigilar. Los mensajes de ventana que normalmente recibiría el botón serán ahora recibidos también por nuestra instancia de la clase derivada de <i>NativeWindow</i>.</div><div>Es cierto que seguimos necesitando crear una clase por herencia. Si necesitamos realizar esta operación de vigilancia con <i>N</i> tipos diferentes de botones, ¿significa que tendremos que crear <i>N</i> clases derivadas de <i>NativeWindow</i>? Resulta que no: podemos definir una sola clase derivada de <i>NativeWindow</i> sin tener conocimiento del control concreto que vigilaremos y las acciones concretas que queremos ejecutar. Lo que haríamos en su <i>WndProc</i>, sencillamente, sería llamar a una variable de tipo <b>delegate</b> que especificaríamos al crear las instancia de dicha clase. Y sería ese delegado donde implementaríamos el código necesario:</div><pre class="p"><b>private sealed class</b> <font color="darkcyan">PeepingTom</font> : <font color="darkcyan">NativeWindow</font>, <font color="darkcyan">IDisposable</font><br />{<br /> <b>public delegate void</b> WindowProc(<b>ref</b> <font color="darkcyan">Message</font> m);<br /><br /> <b>private</b> <font color="darkcyan">WindowProc</font> tommy;<br /><br /> <b>public</b> PeepingTom(<font color="darkcyan">Control</font> ladyGodiva, <font color="darkcyan">WindowProc</font> tommy)<br /> {<br /> <b>this</b>.tommy = tommy;<br /> <b>this</b>.AssignHandle(ladyGodiva.Handle);<br /> }<br /><br /> <b>public void</b> Dispose()<br /> {<br /> <b>this</b>.ReleaseHandle();<br /> }<br /><br /> <b>protected override void</b> WndProc(<b>ref</b> <font color="darkcyan">Message</font> m)<br /> {<br /> tommy(<b>ref</b> m);<br /> <b>base</b>.WndProc(<b>ref</b> m);<br /> }<br />}</pre><div>Los nombres, por supuesto, hacen referencia a la historia de <a href="http://public-domain-images.blogspot.com/2010/06/lady-godiva-by-john-collier.html" target="_blank">Lady Godiva</a>, que se paseó desnuda a caballo por todo Coventry para obligar a su marido a bajar los impuestos (¡que no se le ocurra la idea a la Sonsoles, please!), y a un tal Peeping Tom, o Tomasito el Mirón, que desobedeciendo las órdenes del cabreado marido, se escondió para regocijarse con la visión de la damita en pelotas.</div><div>Suponga ahora que tenemos un formulario con un botón, y que queremos saber cuándo el ratón pasa por encima del botón. Para asignarle un espía al botón, dado el diseño de nuestra clase, tenemos que esperar al evento <i>Load</i> del formulario, para asegurarnos de que el identificador de ventana del botón haya sido creado. En ese momento, hacemos algo parecido a esto:</div><pre class="p"><b>private void</b> Form1_Load(<b>object</b> sender, <font color="darkcyan">EventArgs</font> e)<br />{<br /> <b>new</b> <font color="darkcyan">PeepingTom</font>(button1,<br /> <b>delegate</b>(<b>ref</b> <font color="darkcyan">Message</font> m)<br /> {<br /> <b>if</b> (m.Msg == 0x02A1)<br /> <b>this</b>.Text = <font color="maroon">"Mouse hover"</font>;<br /> <b>else if</b> (m.Msg == 0x02A3)<br /> <b>this</b>.Text = <font color="maroon">"Mouse leave"</font>;<br /> });<br />}</pre><div>El ejemplo no es muy bueno, porque para esta tarea en particular, ya tenemos eventos en la clase <i>Button</i>, pero espero que sirva para que se haga una idea.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com1tag:blogger.com,1999:blog-22471446.post-25819741837726840192010-09-04T17:29:00.008+02:002010-09-04T19:56:55.045+02:00Una ventana dentro de otra<div><img style="float:right; margin:1px 0 1px 8px; border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoqrWCJMFQjf1quGATFEspX2j88rY-3AVEhsGrSiAXAP_s6nsCQuMKoPb0Aru_V1viWNh6UmTmlVGpojKAM5ifNeB37Dd7X3h2f4wCaYhdaVdbG_SG5KOC3s2fmFyrbqR8AF7I/s320/magritte.jpg" border="0" alt="La Condition Humaine, de René Magritte" /><span class="first">Este es un truco rápido y</span> sencillo que, sin embargo, es difícil encontrar a la primera buscando en Internet:</div><ul><li>¿Cómo meter una ventana dentro de otra?</li></ul><div>¿Y para qué le puede hacer falta tal cosa? Un uso evidente sería crear un sistema de <i>docking</i>, que permita agrupar dinámicamente varias ventanas dentro de un contenedor; naturalmente, saber cómo meter una ventana independiente dentro de otro control es sólo el comienzo del proceso.</div><div>Pero existe otro uso más mundano: imagine que está creando un administrador de ventanas. Teóricamente, el administrador debe permitirle cambiar el estilo de la aplicación a petición del usuario: de ventanas SDI individuales, a una interfaz basada en pestañas e incluso a un sistema MDI. ¿Cómo deberían implementarse las ventanas para que pudiesen utilizarse con este gestor? Una posibilidad, que he utilizado en varios proyectos, es que las "ventanas" se diseñen como controles de usuarios (<i>UserControl</i>). El motivo es lo fácil que se añade un control dentro de otro, ya sea en tiempo de diseño o de ejecución.</div><div>Sin embargo, no es tan evidente cómo meter una instancia de la clase <i>Form</i> dentro de, digamos, un <i>TabPage</i>. Si lo intenta, sin más, provocará una excepción advirtiendo que "no se puede meter un control de nivel superior dentro de otro". El error, por fortuna, nos indica también la solución. Hay una propiedad poco conocida de la clase <i>Form</i>, que hay que poner a <b>false</b>. Por ejemplo, si queremos meter una instancia de la clase <i>Form2</i> dentro de una nueva página de un <i>TabControl</i>, necesitaremos algo parecido a esto:</div><pre class="p"><b>var</b> f2 = <b>new</b> <font color="darkcyan">Form2</font>();<br />f2.TopLevel = <b>false</b>;<br />f2.FormBorderStyle = <font color="darkcyan">FormBorderStyle</font>.None;<br />f2.Dock = <font color="darkcyan">DockStyle</font>.Fill;<br /><b>var</b> tp = <b>new</b> <font color="darkcyan">TabPage</font>(<br /> tabControl1.TabPages.Count.ToString());<br />tabControl1.TabPages.Add(tp);<br />tp.Controls.Add(f2);<br />f2.Visible = <b>true</b>;</pre><div>Aquí he metido la ventana, que puede haber sido diseñada al antojo del programador, dentro de un control de pestañas. Pero es igualmente posible meterla directamente dentro de un prototipo vacío de ventana hija MDI... o lo que se le ocurra.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com11tag:blogger.com,1999:blog-22471446.post-69301808720609618212010-08-22T20:27:00.016+02:002010-08-22T21:44:07.664+02:00Validación en Entity Framework<h3>Metodologías e ideologías</h3><div><span class="first">Debo comenzar confesando</span> que la moda <i>domain-driven</i> me parece ridícula. Es cierto que la programación orientada a objetos es estupenda… y que el modelo relacional también lo es. También es cierto que, al mezclarlos, se produce un desajuste que en inglés llaman <i>impedance mismatch</i>. Pero los intentos de "resolver" el problema mediante la metodología <i>domain-driven</i> consisten, básicamente, en cargarse todas las bondades del modelo relacional, de la arquitectura cliente-servidor y, de paso, la mayoría de los avances aparecidos y probados en estos cuarenta años de bases de datos relacionales.</div><div>No es éste el lugar para explicar todo lo que tengo en contra del DD, por lo que me limitaré a un solo argumento. ¿Existe alguna diferencia básica, digamos, entre una aplicación "de negocios" y una aplicación, digamos, como Word o un sistema operativo? Claro que la hay: el dinamismo de las llamadas reglas de negocio. Usted puede escribir una clase para un procesador de texto y atiborrarla de reglas sobre el tratamiento de las mayúsculas. Probablemente, dentro de cinco años esas reglas seguirán siendo igual de malas o apropiadas. En cambio, las reglas para el funcionamiento de una aplicación de negocios cambian constantemente.<img style="float:right; margin:1px 0 1px 8px; border-style:none; padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid3UgUDvW4vLIMHtMBPbBZTZREFMHRP5lcxR7LkjMLP-VIJWpqCZxAjYinx0Ojw2B2Z0PL_I4ogKMdvSb1P4krj8l2l1d1FvjLSkE8e8WKJ2Qkw8WeiqVL1teeQJTdBrBEM6mP/s320/carl.jpg" border="0" alt="Este tío cree en el domain-driven..." /> Si usted enchufa esas reglas como parte de métodos de las clases "de negocio", tendrá que contratar a un <i>nerd</i> granujiento a jornada completa, para que cada vez que el dueño del negocio se cambie los gayumbos, cambie la programación de las clases afectadas, recompile y reinstale el sistema.</div><div>Por supuesto, esto es lo que todo <i>nerd</i> desea: un trabajo fijo a jornada completa, ya sea cambiando detallitos insignificantes de la clase <i>Order</i> o "tuneando" el núcleo de su instalación personal de Linux, que le deje tiempo para conectarse al Facebook y para jugar a dragones y mazmorras. Es comprensible. Otra cosa es que sea racional o eficiente. O bueno para alguien excepto para el propio <i>nerd</i>.</div><h3>La cabra y el monte</h3><div><img style="float:left; margin:1px 8px 1px 0px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHvEJ8aw_p2zxvuo-JW5BT-77vtJuNaJY5TDl2JpZdTBkto7rLTTBsGCkImnatQfKwHkkpDyj58uIBrijLv5dLjDOhTPRxj6gKlWrUThGSF-xrst2HTbW0gVTafeNXwpI4LXBR/s320/goat.jpg" border="0" alt="Sí, se parece probablemente a algún compañero de trabajo" />Las modas, al final, visten a personas reales. Quiero decir, que si al artista se le olvidó la ranura en el pantalón para hacer pis, el portador de la prenda ya se las arreglará para orinar; eso, o reventará. Por mucho <i>domain-driven</i> que haya infectado la cabecita del pobre programador, en cuanto necesite hierba, la cabra irá al monte… o reventará, ya sabe. ¿Un ejemplo? Teóricamente, en el mundo ideal de nuestros <i>guitar heroes</i> con acné, cada clase debería ocuparse de su propia validación. Pero, ¡oh, qué penita!, los programadores reales que trabajan con Entity Framework han adquirido la costumbre de acumular validaciones dentro del código del método <i>SaveChanges</i> (hay varias formas de conseguirlo).</div><div>¿Es eso bueno, o malo? Hay de todo. Esperar a <i>SaveChanges</i> para comprobar que una fecha de nacimiento no pertenece al siglo XII, por ejemplo, es una soberana estupidez. Pero si la regla depende del estado de una entidad, no le queda más remedio que enredar con el administrador del estado: la pureza <i>domain-driven</i> exige que este estado sea externo a la entidad. Por ese motivo, era tan complicado utilizar entidades con servicios WCF en la versión anterior de .NET. Y por ese motivo, gracias al cielo, Microsoft ha terminado ciscándose en la pureza y regalándonos <i>self-tracking entities</i> en la nueva versión. Viva lo impuro cuando resuelve problemas.</div><div>¿Y qué dice la sacrosanta teoría sobre todo esto? En honor a la verdad, la Programación Orientada a Objetos dice que, en este caso particular, la centralización es mala. Y tiene razón… si se tiene en cuenta el contexto sobre el que se desarrolla esta metodología: un mundo en el que las principales extensiones a una aplicación se producen principalmente mediante la incorporación de nuevas entidades. Pero este no es el escenario característico de una aplicación de ventas, de préstamos o de Bolsa, donde es más probable que aparezcan nuevas reglas para los viejos objetos que nuevos objetos con nuevas reglas. El impacto de la modificación de las nuevas reglas es mayor si el código que las controla está disperso a lo largo y ancho de los ficheros individuales de clases de entidades.</div><div>Y hay más: observe que estas reglas de negocio suelen referirse al estado observable de las entidades de negocio. Traducido a código fuente: las reglas de negocio, por lo general, afectan a propiedades públicas de estas clases. Al fin y al cabo, estas reglas son establecidas por analistas funcionales… o por el propio jefe del cotarro; es decir, por gente que no tiene por qué enredarse con <b>protected</b>, <b>private</b> e <b>internal</b>.</div><h3>Cabalgando la ola</h3><div>La centralización, por lo tanto, no siempre es mala; digamos, entonces, que aceptamos que una parte de las reglas de negocio se implementan como parte del mecanismo de sincronización de los cambios con la base de datos. Sin embargo, ¡seguimos necesitando al <i>nerd</i>! Para cada cambio en las reglas, sigue siendo necesario recompilar la aplicación… y redistribuirla.</div><div>No sólo eso: imagine que su aplicación la utilizan varias empresas. O incluso, que la utiliza una sola empresa, pero que ésta tiene sucursales en distintos países, con diferentes legislaciones e idiosincrasias. Cada vez que en China cambien las reglas de descuentos para sujetadores, en la sucursal de Groenlandia tendrán que reinstalar el maldito software de ventas.</div><div>¿Y si movemos esas reglas fuera de la aplicación? Hablo de ubicar las reglas en un fichero de texto, de manera que la aplicación lo lea, digamos que al iniciarse. El contenido del fichero sería traducido a código ejecutable (eso es muy sencillo, con toda la tecnología que ofrece .NET) que se ejecutaría como parte del método <i>SaveChanges</i> del contexto.</div><div>Será más sencillo si muestro un ejemplo de reglas. La sintaxis concreta que he utilizado imita a la de C#, pero está claro que las variaciones son posibles:</div><pre class="p"><b>class</b> Order<br />{<br /> <b>on insert</b><br /> Created = DateTime.Now;<br /> <b>on update</b><br /> Modified = DateTime.Now;<br /> <b>rule</b> MustPaidBeforeShipping <b>is</b><br /> Paid || !ShipDate.HasValue;<br />}</pre><div>La explicación:</div><ol><li>Las reglas se definen por clase. Esto es así por la forma en la que puede tener lugar la validación durante <i>OnSavingChanges</i>: el componente que ejecuta el script recorrerá los <i>ObjectStateEntry</i> modificados en el contexto. Para cada uno de ellos, se elegirá el conjunto de reglas a ejecutar o verificar de acuerdo al tipo de entidad asociado, y luego, al estado de la entidad.</li><li>Es recomendable que se respete la herencia. Si una regla se define en una clase base, debe respetarse también en las posibles clases derivadas, siempre que una de estas no la anule.</li><li>Como mínimo, se deben soportar dos tipos de reglas: verificaciones y asignaciones a propiedades. En el ejemplo, hay dos asignaciones a propiedades, separadas según el estado de la entidad, y una sola verificación, que al no mencionar el estado, se activará tanto para inserciones como para actualizaciones.</li><li>Cada regla tiene un identificador asociado. El escenario típico de estas validaciones será un proceso WCF remoto. Imagine que asociamos un mensaje de error directamente con la regla: ¿en qué idioma debería estar? Un servicio de capa intermedia debe evitar atarse a un idioma concreto, porque puede estar siendo utilizado por clientes configurados para idiomas diferentes. De todos modos, debería ser posible sustituir el identificador por un literal de cadena, para casos sencillos en que no se prevé la traducción a otros idiomas.</li><li>Las expresiones trabajarían, principalmente, sobre entidades aisladas, por lo que no habría que preocuparse por extensiones del tipo LINQ: si usted quiere verificar que el total de una orden no sea superior a un valor, por ejemplo, prográmelo <b>donde realmente corresponde</b>, es decir, en la base de datos. Entity Framework no le puede ofrecer garantías, llegados a este punto, de que el grafo de entidades asociados a la entidad modificada, esté completamente cargado en memoria. Sólo piense, por ejemplo, lo difícil que es verificar la unicidad de una columna en un proceso cliente, sin tener todas las entidades en memoria y sin lanzar una consulta adicional al servidor.</li></ol><div>Sobre las expresiones soportadas: en principio, no haría falta complicarlas con índices y corchetes, por las razones antes expuestas. Las expresiones básicas serían constantes y "rutas de objetos" (<i>object paths</i>). Estas rutas siempre comenzarían por una propiedad de la clase que define la regla, o por una clase o espacio de nombres. En nuestro ejemplo, <i>ShipDate.HasValue</i> comienza por una propiedad de la clase <i>Order</i>, pero <i>DateTime.Now</i> es, evidentemente, una propiedad estática de una conocida clase. Este tipo de sintaxis es muy fácil de implementar y traducir.</div><div>Las expresiones, que son el núcleo de nuestro sistema, se traducirían como árboles de expresiones LINQ, que se pueden compilar a código nativo dinámicamente. En esto ya tengo también bastante experiencia: el sistema es sencillo y muy eficiente (lo he utilizado en filtros a la medida para un servidor RTD para Excel, implementado dentro de una aplicación de servicios de Windows).</div><h3>En resumen</h3><div>Se trata solamente, en esta etapa, de una propuesta: crear un componente que, a partir de un script externo, se ocupe de determinadas validaciones e inicializaciones dentro del Entity Framework. Es fácil señalar el método <i>SaveChanges</i> como el punto donde se insertaría el componente de validación. Probablemente existan más puntos donde se pueda actuar.</div><div>En principio, el script con las reglas puede estar ubicado en un fichero de texto. Esto sería apropiado si las aplicaciones clientes accediesen a las entidades a través de un servicio WCF. Sería este servicio quien centralizaría el manejo del script y determinaría las reglas a cumplir. Pero sería sencillo extender el mecanismo, en ausencia de una capa intermedia, ubicando las reglas en la base de datos, para simplificar el cambio de las mismas.</div><div>Por supuesto, escribo esto, en este momento, para recabar sugerencias.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com21tag:blogger.com,1999:blog-22471446.post-24297791702784665452010-06-02T16:01:00.001+02:002010-06-02T16:02:22.250+02:00Música para piano<div>... pues eso: <a href="http://www.marteens.com/media/topsham.mp3">música para piano</a>.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com7tag:blogger.com,1999:blog-22471446.post-23164919073605510832010-03-29T16:35:00.003+02:002010-03-29T16:41:28.029+02:00Trabajo en Delphi<div style="border: 1px solid #0c0c0f; padding: 8px; text-align: center; background-color: #f0f0f0;">Se busca programador en lenguaje Delphi, abstenerse freelance, se valoraran conocimientos en base de datos SQL. Imprescindible Delphi.<br />Empresa situada en Madrid. Enviar curriculum a <a href="mailto:wis@worldwis.es">wis@worldwis.es</a>.</div><div style="font-style: italic; font-size: 85%;">(... y sí: dentro de poco habrá noticias sobre nuevos cursos de C#, y gracias a Marcos Santín, de Delphi, y más...)</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com6tag:blogger.com,1999:blog-22471446.post-58492884983654835242009-07-24T14:39:00.019+02:002009-07-24T16:10:34.711+02:00Cagarse en el chocolate<h3>Serie: Los N Pecados Capitales</h3><div><img style="float:right; margin:1px 0 1px 8px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdbMry3T2Nkze_ooyXxR4MOHl9_Qf0I0QfgsatHLUU2jxi49awXmvYR8SlqSxdJCNjsyEiJYivNakBoyt5C7F0ouVrUOPbwjPn_l1SXXpyk6ylXiyH85V9Ff9iO63CUbCAO1DK/s400/oompaloompasqueenbee.jpg" border="0" alt="Scott McNealy: Zángano Padre de todos los Oompa Loompas de este mundo" /><span class="first">En la empresa de Willy</span> Wonka, se fabrica un exquisito chocolate. Al menos, esa es la opinión general. La producción está a cargo de los umpalumpas: una extraña raza de friquis canijos, adictos a la programación en Java, a los que pagan con granos de cacao.</div><div>Los umpalumpas tienen la curiosa manía de inventarse canciones sobre la marcha, y luego pretender que se trata de tonadas tradicionales, de las de toda la vida. Por ejemplo, si un niño glotón se cae dentro del río de chocolate, improvisan una canción explicando por qué no se debe uno inclinar demasiado sobre la corriente. Si un proyecto de desarrollo fracasa estrepitosamente, se inventan melodías que hacen referencia a dominios escuálidos, programación ágil e inversión del control. Son pintorescos, estos personajillos.</div><div>Pero los umpalumpas tienen también una costumbre muy fea. Alex, un umpalumpa de Pontevedra, revuelve el chocolate fundido cuando siente que algo más se revuelve en su interior:</div><div style="margin-left: 24px; margin-right:24px;">- Willy… ehm… señor Wonka.<br />- ¿Sí, Alex?<br />- Tengo un apretonciño de intestinos…<br />- Te aguantas. Esta barra de chocolate tenía que estar para ayer.<br />- ¡Pero es que me lo voy a hacer encima!<br />- Caga entonces dentro de la tina del chocolate.<br />- ¡¿Cómo?!<br />- Joder, Alejandrito, ¿tengo que dibujarte un diagrama de clases?<br />- Pero, ¿cómo vamos a echar mierda dentro del chocolate?<br />- A ver, ¿de qué color es el chocolate? Marrón. ¿Y la mierda? Marrón. Un poco de mierda espesa aporta crema al cacao.<br />- Pero… es que estoy un poco estreñido y estoy cagando boliñas, como las de las cabras…<br />- Pues mejor aún: chocolate con cacahuetes, y si las bolas son más grandes, con nueces y avellanas.</div><div>Por desgracia, la mierda de umpalumpa es sumamente corrosiva. Por su culpa, caen aviones, revientan naves espaciales, fallan sistemas de soporte vital en hospitales y se aplican dosis excesivas de radiación a pacientes de radioterapia. Será mierda, pero hay que tomársela muy en serio.</div><div>Hay mil y una maneras de cagarse en el chocolate. Por mencionar unas pocas:</div><ol><li>Una aplicación para almacenar expedientes médicos, escrita en dBase, generaba claves primarias mediante el sucio mecanismo de contar los registros en el fichero y aumentar el valor en uno. Mientras el hospital tuvo un único ordenador en admisión, el chocolate sabía a ambrosía. Un día instalaron un segundo ordenador, y en algún momento, se crearon dos registros simultáneamente… con la misma clave primaria. Migrando la aplicación a InterBase, en el 2000, alguien tropezó con la historia clínica de un buen señor que había sufrido la extirpación de sus ovarios.</li><li>En la historia anterior, el error no se detectó porque los índices de aquella versión de dBase no permitían la comprobación de unicidad. Pero conozco un par de sistema implementados sobre Oracle en los que los “diseñadores” (es un decir) han evitado el uso de claves primarias concienzudamente.</li><li>La mismísima Borland se cagó, en su momento, en el chocolate de la casa. Cuando apareció Delphi 2, se produjo una ola de fallos en aplicaciones de servidor. La causa: en servidores con procesadores Xeon, fallaban determinadas operaciones sobre cadenas de caracteres, porque la implementación del tipo <i>String</i> no era <i>thread-safe</i>.</li><li>Todos sabemos, o eso espero, que no debemos acceder a los métodos de un objeto de ventana desde un hilo diferente a aquel en el que fue creada. Sin embargo, este tipo de violación no es verificada en ningún caso por .NET Framework v1.1. Muchas aplicaciones, por lo tanto, ignoran total o parcialmente el problema… hasta que migran a una versión más reciente de la plataforma y empiezan a reventar por todas partes. Entonces, improvisan cantando los umpalumpas, la culpa es de Microsoft, evidentemente.</li></ol><div>¿Se ha dado cuenta ya de cuál es el patrón emergente? Se trata siempre de fallos que se producen al no tener en cuenta la concurrencia. La técnica ingenua de generación de claves primarias funciona mientras hay un solo ordenador. La implementación defectuosa de las operaciones de cadenas funciona incluso cuando hay varios hilos, pero siempre que exista un único núcleo. Los errores de reentrancia de Windows Forms y WPF se producen pero no se notan… hasta que el sistema revienta por causas desconocidas.</div><div>Cuando se trabaja con una base de datos relacional, además, en teoría existe una forma muy sencilla de evitar la mayoría de los errores: trabaje con transacciones serializables. ¡Pero la mayoría de los programadores, o desconocen su existencia, o tienen miedo a la pérdida de velocidad! De hecho, la mayoría de las operaciones complejas en las bases de datos habituales pueden implementarse con un nivel de aislamiento más bajo, si se ordenan inteligentemente las instrucciones. Esto es algo, sin embargo, que se encuentra fuera del alcance de la mayoría. Y quien sospecha que hay algo que puede fallar en su implementación, termina cagando dentro del chocolate: ya haremos algo cuando falle.</div><div>Otra fuente habitual de tropezones en el cacao: los errores de redondeo. ¿Cuántos programadores, de los que trabajan con aplicaciones financieras, tienen idea sobre los problemas de estabilidad numérica y sus consecuencias en el desarrollo? Es más fácil tener una idea vaga de que existe un problema "teórico", y dejar que sea el desdichado usuario el que hunda sus dientes en la mierda de umpalumpa. Sólo entonces merece la pena hacer algo.</div><div>¿Existe alguna solución mágica para evitar sustancias extrañas en nuestros alimentos? Por desgracia, sólo hay una forma, y requiere una cualidad personal muy extraña y poco valorada: la ética profesional. Pero estamos en crisis, y la ética es un bien caro y escaso. Al menos, deberíamos presionar a las autoridades europeas para que los productos de software lleven una etiqueta de advertencia, como las de los sándwiches que venden en las gasolineras:</div><div style="margin-left:24px;margin-right:24px;font-style:italic;text-align: center;">"Puede contener trazas de cacahuetes, pescado, huevo, gluten<br />y mierda de umpalumpa."</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com36tag:blogger.com,1999:blog-22471446.post-28455387794255739872009-07-22T11:08:00.002+02:002009-07-22T11:10:24.312+02:00Libro sobre Delphi Prism<a href="http://www.markdelphi.oamm.info/?p=79">Nuevo libro sobre Delphi Prism</a><br /><br />Ahora tengo prisa (reunión con cliente), pero luego editaré el post, e incluiré fotos y ampliaré el texto. ¡Libro recomendado, por supuesto!Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com24tag:blogger.com,1999:blog-22471446.post-86900694060988521742009-07-20T20:36:00.003+02:002009-07-20T20:42:44.371+02:00El Bien y el Mal<i>Lo único necesario para que el Mal triunfe, es que los hombres buenos no hagan nada.</i><br />(cita atribuida, erróneamente, a Edmund Burke, pero que no por ello es menos cierta)<br />(y no, no tiene nada que ver con la Informática)<br /><hr><br /><div>Me descuido un poco... y veo que se me acumulan 211 comentarios en el último post. Eso es bueno, por supuesto: muchas gracias. Siento no haber estado disponible: estoy con el libro, el curso de EF y un software a entregar para ayer.</div><div>Y ahora pregunto yo: ¿sería buena idea si montásemos un foro? Principalmente para soporte y esas cosas. Me comprometo a entrar regularmente. Y tendría que haber una carpeta para "off-topics", que si no, sería demasiado aburrido. Tengo que preguntar primero a los que alojan mi página (los buenos amigos de la Delphi Magazine) sobre softwares compatibles con sus servidores, por supuesto...</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com11tag:blogger.com,1999:blog-22471446.post-25711164065376328332009-04-15T16:04:00.006+02:002009-04-15T20:11:11.092+02:00Una decepción y un rayo de esperanza<h3>La decepción</h3><div><span class="first">Apartaos de este libro</span> cuanto podáis: <a href="http://www.amazon.com/gp/product/0321564162?ie=UTF8&tag=callindrmarte-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0321564162">Essential LINQ (Microsoft .NET Development Series)</a><img src="http://www.assoc-amazon.com/e/ir?t=callindrmarte-20&l=as2&o=1&a=0321564162" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />. Ni se me ocurriría comprar un libro de Calvert (ya he tenido la mala experiencia), pero pensé que al estar Dinesh Kulkarni en el potaje, la cosa cambiaría. ¡Qué error!:</div><ul type="disc"><li>Las dos terceras partes del libro son las típicas obviedades sobre LINQ for Objects que puede encontrar en <a href="http://www.marteens.com/pdfs/csharp_intsight.pdf" target="_blank" title="Libro en PDF sobre C#">Intuitive C#</a> gratuitamente, o en <a href="http://www.marteens.com/forms/linqform.htm">el libro de Octavio sobre LINQ</a> por una fracción del precio. Es el tipo de énfasis que hace un autor que odia las bases de datos y sólo le interesa hacer trinos y gorgoritos con el lenguaje.</li><li>El contenido sobre LINQ to SQL (un sistema ya condenado) es el doble o el triple (me niego a contar páginas) que el contenido sobre el Entity Framework.</li><li>... pero ni siquiera el contenido sobre LINQ to SQL es medianamente funcional, así que ya puede imaginar lo que hay sobre el EF.</li><li>Charlie Calvert nos obsequia con sus mañas características de escritor: apéndices sobre todo lo humano y divino (para engordar el libro y encarecerlo), incluyendo una tabla de seis páginas sobre los atajos de teclados en Visual Studio 2008 (que usted puede encontrar perfectamente en la ayuda de VS).</li></ul><h3>El rayo de esperanza</h3><div><iframe src="http://rcm.amazon.com/e/cm?t=callindrmarte-20&o=1&p=8&l=as1&asins=059652028X&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr&nou=1" style="width:120px;height:240px; float: left; margin: 1px 8px 1px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe><span>Este otro libro</span>, <a href="http://www.amazon.com/gp/product/059652028X?ie=UTF8&tag=callindrmarte-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=059652028X">Programming Entity Framework</a><img src="http://www.assoc-amazon.com/e/ir?t=callindrmarte-20&l=as2&o=1&a=059652028X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />, de Julia Lerman, es harina de otro costal. Para empezar, tiene 792 páginas con un tipo de letra legible, pero compacto. Nada de trucos sucios para engordar artificialmente el contenido.</div><div>En segundo lugar, se tratan todos los temas necesarios: ESQL, los puñeteros formatos de bajo nivel para crear un esquema conceptual y mapearlo a una base de datos, el uso avanzado de procedimientos almacenados, los problemas actuales al intentar trabajar en múltiples capas (¡sinceridad, por una vez en la vida!), cómo usar EF desde ASP.NET y Windows Forms, y los problemas y soluciones en cada plataforma...</div><div>Además, y esto es muy importante, la autora escribe bien. No he terminado el libro, y me va a llevar un par de semanas, pero hasta donde he llegado, me siento satisfecho.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com210tag:blogger.com,1999:blog-22471446.post-43991743763107042352009-04-04T15:39:00.023+02:002009-04-04T22:27:49.319+02:00Perestroika<div><img style="float:right; margin:1px 0 1px 8px; border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeuaaRL1_Dc3qjFKrcLNKp7RCbQY4SszzJCajlhWAFufKbDE2DseZmB9W20U7daMXW4kQofRQWDBbzAMmfjYnAqEHkyPSCC0bs7p7rMC3kK0T07cjbD_XtMFFFkCDd7GUKnBNh/s400/marxismfowlerism.jpg" border="0" alt="¡Gloria eterna al marxismo-fowlerismo!" /><span class="first">El funcionario de la calva</span> brillante con la mancha como un mapamundi se levantó y se dirigió hacia los micrófonos. Carraspeó y comenzó a hablar cansinamente, como hablaban los funcionarios de la extinta Unión Soviética:</div><div style="margin-left: 24px;">- <i>Daraguíe tavarischi</i> - es decir, "hola, troncos" - El Soviet Supremo me ha encargado la honrosa tarea de comunicaros dos noticias. Una buena y una mala. La buena es que hoy ya es viernes.</div><div>Un murmullo de satisfacción teñida de falsa sorpresa recorrió la sala.</div><div style="margin-left: 24px;">- La mala - titubeó, aunque sólo un momento - la mala es que lo nuestro no funciona.</div><div>Los ecos del murmullo cesaron abruptamente.</div><div style="margin-left: 24px;">- ... y no me refiero a lo de enfriar en una nevera las semillas de patata antes de sembrarlas, para aclimatarlas al frío - el anciano hijo de <a href="http://en.wikipedia.org/wiki/Trofim_Lysenko" target="_blank">Trofim Lysenko</a> se levantó airado y abandonó la asamblea - sino a este invento del comunismo.</div><div>Una mosca atravesó la enorme sala zumbando. Nadie la detuvo.</div><div style="margin-left: 24px;">- Sí, coño, hemos estado haciendo el panoli durante setenta años. Y para nada. Hemos matado de hambre a media Ucrania. Hemos jodido a conciencia a polacos, húngaros y checos, y a buena parte de Alemania. ¿Y todo para qué? Para que nuestros obreros pasen hambre y frío, para fabricar estos horribles coches y esos espantosos sintetizadores alemanes democráticos que se funden antes de terminar la primera estrofa de la Kalinka. Esto, <i>maí vesyolye rebyata</i>, no firula...</div><div>Los murmullos se reanudaron. El delegado de Dirkadirkastán alzó tímidamente la mano y preguntó:</div><div style="margin-left: 24px;">- Y ahora, ¿qué?<br />- Ahora - el calvo hizo una pausa teatral - ahora... ¡perestroika!</div><hr class="p"><div><span class="first">En Redmond parecen</span> estar también algo revueltos y perestroikos:</div><ul type="square"><li><a href="http://blogs.msdn.com/efdesign/archive/2009/03/24/self-tracking-entities-in-the-entity-framework.aspx" target="_blank">Self-tracking entities in the Entity Framework</a></li></ul><div>Y ya era hora, coño. Ya era hora de que alguien se diese cuenta de que todo este invento del <a href="http://en.wikipedia.org/wiki/POJO" target="_blank">POJO</a>, del POCO y del MOJO POCO no funciona cuando se trata de escribir aplicaciones multicapas. La vanguardia del proletariado, por supuesto, pondrá el grito en el cielo, pero de momento andan callados. Puede que no hayan comprendido lo que hay en juego. En realidad, los fundamentalistas javalinos suelen comprender muy pocas cosas.</div><div>Es verdad que los de Microsoft se asemejan más a los chinos que a los rusos: cada cierto tiempo se inventan una Revolución Cultural y no son muy ortodoxos en lo tocante al martinismo-fowlerismo. Además, acaban de perder al Gran Timonel. Y tienen una misma palabra para <i>crisis</i> y <i>oportunidad</i>: suena parecido a <i>crisistunidad</i>.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com84tag:blogger.com,1999:blog-22471446.post-70926038042146333282009-03-06T22:16:00.002+01:002009-03-06T22:19:33.866+01:00Mientras tanto...<div><span class="first">... y porque no solo</span> de pan vive el hombre, un poco de rock instrumental, hecho en casa:</div><ul type="square"><li><a href="http://www.marteens.com/media/everydayshero.mp3">Everyday Hero</a></li></ul><div>Dedicado a todos los que tienen que enfrentarse día a día con su correspondiente Mr. Butters.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com11tag:blogger.com,1999:blog-22471446.post-24206720515890344962009-03-02T18:53:00.010+01:002009-03-03T07:39:44.432+01:00Mr. Butters (I)<div><span class="first">Mr. Butters es un</span> "experto" imaginario, que trabaja en una empresa imaginaria, y que "controla mogollón" sobre bases de datos, aunque la única base de datos con la que ha trabajado en su puñetera vida es Oracle 9, utilizando sólo los trucos que aprendió para Oracle 7. Es un tipo que desconfía de las <i>foreign keys</i>, que considera peligrosos los <i>triggers</i> y los procedimientos almacenados, y que prefiere que el acceso a datos se haga con ODBC... sobre todo por compatibilidad, coño, que somos una empresa seria.</div><img style="DISPLAY: block; MARGIN: 1px auto 10px; WIDTH: 480px; padding:0px; border-style:none; TEXT-ALIGN: center" alt="Mr. Butters, Database Expert, Lucky You!" src="http://www.marteens.com/blogs/mrbutters1.jpg" border="0" /><div>Mr. Butters, por supuesto, es sólo un producto de mi febril imaginación. Cualquier coincidencia con la realidad, es un mero resbalón de mis neuronas.</div><div style="font-style:italic;font-size:90%;line-height:normal; margin-left: 24px;"><span style="font-style: normal; font-weight: bold;">The Waste Brain</span><br /><br />Mr. Butters is the silliest dork, breeding<br />Incompetence out from the dead land, mixing<br />Idiocy and arrogance, stirring<br />Foolishness with an amazing lack of common sense.<br /><br />Visual Basic kept us warm, covering<br />Bugs in forgetful snow, feeding...<br />(etc, etc)</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com47tag:blogger.com,1999:blog-22471446.post-41858719025983631022009-02-28T18:01:00.004+01:002009-02-28T18:33:03.688+01:00Ajedrez<a href="http://www.marteens.com/xsight/images/chess800.png" target="_blank"><img style="display:block; margin:0px auto 10px; text-align:center;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdzKRjlGmHwx5ETU_FeMFmjLqsPdVYX6Ah4raui7cQMuuVtMyVy-tmULc5JUfoAsgqgc0IV7Ba_mStaC_GQtdeV8l5rd_BNMwknfhmZaihWBhO1Fk8MjN690vVSh-WqDMkA92V/s320/chess320.png" border="0" alt="Hyperbolic surfaces, ambient occlusion, diffuse reflections, area lights, focal blur" /></a>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com7tag:blogger.com,1999:blog-22471446.post-25337085538196958162009-02-18T00:24:00.012+01:002009-02-18T15:41:33.604+01:00Otra seta, perdón, libro sobre LINQ<div><iframe src="http://rcm.amazon.com/e/cm?t=callindrmarte-20&o=1&p=8&l=as1&asins=1590599659&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr" style="float: left; width:120px;height:240px; margin: 1px 8px 1px 0;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe><span class="first">Cuando comparo este libro</span> con una seta (y la seta sale ganando al final) no es sólo por la capacidad de propagación por esporas de los libros sobre LINQ sino, principalmente, por su potencial alucinógeno. Leo un par de páginas y alucino. Luego viene la resaca. Pasa un par de horas y siento el mono de volver a comprobar cuán malo es el libro. Y así, entre risas y risas, me lo he leído. Pero las setas tienen fibra y son buenas para el colon.</div><div>Vale, me he pasado. Si a usted le cuesta leer en pantalla, este libro puede sustituir a la ayuda en línea, siempre que renuncie al índice, a las búsquedas y a ese estilo seco de los documentos de Microsoft que tanto gusta a los amantes del dolor. De todos modos, supongo que tengo que justificarme. Ahí va eso...</div><div>El principal problema de (joder, qué pedazo de título)... bueno, de este título, es la bisoñez del autor, acompañada de un exceso de entusiasmo, buenrrollismo y grandes dosis de soberbia injustificada. Este es un libro de apenas 380 páginas, con los típicos listados sin abreviar y con un tipo de letra más grande que la del texto. A pesar de todo esto, el autor se permite escribir un ditirambo de cuarenta páginas en honor a los ORM... demostrando de paso que no tiene ni pajolera idea del asunto. Ahí va un sabroso "tip" (página 5):</div><div style="background-color: #f0f0ff; padding: 4px 8px 4px 8px; font-size: 90%;line-height:105%;"><b>Note</b>: Writing stored procedures does not equate to bad design. On the contrary, stored procedures if managed correctly are an excellent alternative to dynamic SQL. If you use a code generator to create your CRUD stored procedures, or if you develop them by hand and have good oversight, in many cases you will be able to use them in conjunction with an ORM tool.</div><div>¡Gracias, gran Vijay (así se llama el pavo), por perdonar nuestras viles existencias! Pero siento interrumpir: usted, ¿ha entendido algo? ¿Son buenos o no los procedimientos almacenados? ¿Sólo si se escriben a mano, o si los crea una herramienta? Ah, ¿en ambos casos? Entonces, ¿para qué mencionar que mañana lloverá o no? El problema está en que el Gran Vijay "sabe" que los procedimientos almacenados sólo son buenos si sirven a los propósitos del Gran ORM. Pero soltar tal chorrada es peligroso. Te puede enemistar con esa bestia cejijunta del DBA (en cristiano, el Administrador de Bases de Datos). Los DBA's, cuando se enfadan, te dejan sin índices secundarios y te echan sal en el café.</div><div>"Claro, Marteens, cabronazo, pero nos has ocultado el contexto de la parida". Bien aquí tiene el contexto: el señor Mehta nos explica que la compañía X ha escrito software para un fabricante de comida para perros. ¿Y sabe qué?, pues que usaron procedimientos almacenados. La cosa se les escapa de las manos (claro, porque son unos pecadores relacionales). Y de repente, llega la gran oportunidad: pueden vender la aplicación a un fabricante de comida para gatos. Aquí viene la Segunda Parida:</div><div style="background-color: #f0f0ff; padding: 4px 8px 4px 8px; font-size: 90%;line-height:105%;">With the increased development costs, Company X won't be making enough money to justify this deal [...] so they hire a consulting company to help reduce their overhead.</div><div>Vijay, hermanito, que nunca se te ocurra montar una empresa con los ahorros de papá, que te los vas a fundir. La consultoría, evidentemente, les endiña un ORM a la Compañía X, y según palabrita del niño Vijay, <i>(they) optimize and normalize the company database</i>. Lamento señalar que:</div><ul><li>Lo de "optimizar y normalizar" suele resultar bastante costoso. Es como empezar de cero. Para que resulte viable, es obligatorio que la consultoría subcontrate programadores en algún país remoto del Tercer Mundo. Quizás los tiros vayan por ahí.</li><li>Ah, de repente la base de datos carece de "normalización". ¿Esa es una consecuencia de utilizar procedimientos almacenados?</li></ul><div>Pero la diversión continúa:</div><div style="background-color: #f0f0ff; padding: 4px 8px 4px 8px; font-size: 90%;line-height:105%;">Company X is able to eliminate the thousands of stored procedures (<i>que ya funcionaban</i>) and instead use "automagic" mapping features to generate its SQL. Development costs go down, profits go up and everyone is happy [...]</div><div>... especialmente el niño Vijay, que ya tiene el ejemplo canónico que siempre se escribe en los primeros capítulos. Menos mal que enseguida nos aclara que <i>"this is an oversimplified example"</i>. Menos mal. Porque cuando la magia entra por la puerta, la inteligencia salta corriendo por la ventana.</div><div>Vale, reconozco que los escritores solemos rellenar los primeros capítulos con la misma alegría con la que los chinos echaban melamina en la leche para bebés. El problema es que los problemas continúan a lo largo del libro... excepto en las páginas que contienen información irrelevante, o en las que se limitan a tirar de la ayuda. Para no hacer esta masacre demasiado larga, aquí tiene un botón de muestra, sumamente divertido y revelador. El hermanito Vijay intenta explicarnos por qué prefiere no usar <b>var</b> para declarar variables locales con tipo implícito (p. 149):</div><div style="background-color: #f0f0ff; padding: 4px 8px 4px 8px; font-size: 90%;line-height:105%;">The reason to be explicit with the types is the same reason that you strongly type variables in the world of software development: to make it easier to understand <b>and gain a negligible performance increase</b>.</div><div><img style="float:right; margin:1px 0 1px 8px;padding:0px;border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH3z8M3-4WmnJOnuW_n_EAk0HvyybS2lNe7lae4CIKVMyZIvAjehWOSxpwj1cKOXJJ0gPQAe7pBEc-hVmc3UcQ5Fd8OYoNyLMUUAZhtVeUal5I21vgy8H8mH79Lb2RrjaEuh1x/s320/puppy.jpg" border="0" alt="Heeeelp meeee!" />Las negritas son mías, por supuesto: resulta que este señor no comprende un sencillísimo truco del lenguaje en que programa, y sin embargo, le recomienda pasar a un sistema que exige un conocimiento en profundidad de ese mismo lenguaje y de su modelo de objetos.</div><div>Claro, que si usted se siente molesto conmigo puede dar ejemplo de solidaridad y comprarse el engendro. Pero recuerde que, cada vez que alguien compra un libro malo, el Diablo aparece y mata un cachorrito.</div><hr class="p"><div style="font-size: 90%; font-style: italic; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">La verdad es que me extrañó un poco el ejemplo de la comida para perros. Pero luego recordé que son los musulmanes quienes consideran que el perro es un animal impuro. Los hindúes, en realidad... bueno, no recuerdo si los consideran seres divinos o si se los comen.</div><div style="font-size: 90%; font-style: italic; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">Hat tip to Alfredo Novoa: <a href="http://www.diariodelaltoaragon.es/NoticiasDetalle.aspx?Id=556121" target="_blank">se casan con ellos</a>.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com147tag:blogger.com,1999:blog-22471446.post-36177914254293482322009-02-09T02:07:00.012+01:002009-02-09T09:32:44.562+01:00Garantías<div><a href="http://www.britannica.com/EBchecked/topic-art/342664/88867/Patricia-Bourne-training-lions-to-stand-on-their-hind-legs"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgeiRqVGgl3tWgBdwyZflsDLYTzy5AYT_iJMaw23WiDLYAgbXYUq0ofp7C5WuQfhhkG7Fpz2rZuiF6yinakpQnanbn8UbBYDP-XF9-GRFGR7YjAEsuJh32r5NQ4uv-Kjgux-1K/s320/lions.jpg" border="0" alt="Dorothy se enfrenta a la familia política del León Cobarde" /></a><span class="first">La Programación no es</span> trabajo ni para cobardes ni para desmemoriados. La máquina es una bestia salvaje atada por una cadena: si queremos ganarnos la vida con ella, tenemos que despojarnos del miedo irracional. Pero también tenemos que recordar, en todo momento, cuál es la longitud de la puñetera cadena.</div><div>Cuando hablo de "cadena", usted debe entender "contrato": cada pieza de software debe dejar bien claro qué es lo que hace por nosotros, siempre que le proporcionemos lo que especifica el contrato. <i>Math.Sqrt</i> nos calculará la raíz cuadrada de un número, siempre que le proporcionemos un número no negativo. El indexador de la clase genérica <i>Dictionary</i> nos devolverá el valor asociado a una clave, aunque sólo cuando la clave se haya almacenado antes. Ignorar la existencia de estos contratos nos puede llevar a uno de dos problemas:</div><ol><li>Pedimos la raíz cuadrada de menos uno, y el león nos come una oreja.</li><li>El problema anterior suele pasar una sola vez. El aprendiz de domador suele quedar aterrorizado, y de ahí en adelante no se acerca al león, excepto cuando su abogado está presente (para que se coma al león en caso necesario). El abogado, por desgracia, termina siendo un problema mayor que el propio león.</li></ol><div>A continuación, voy a presentarle varias joyas que muestran los problemas que se pueden presentar cuando el programador decide ignorar, por excesivo terror, las garantías que le ofrecen los contratos. El problema con estas "joyas" es que, cuando uno se las encuentra, pierde un tiempo valioso intentando decidir si:</div><ol><li>Se trata de una genialidad.</li><li>O se trata de una simple cagada.</li></ol><div>El 99% de los casos corresponde a la segunda opción, pero uno se niega a perder tan pronto la fe en el género humano.</div><div>La primera perla negra tiene que ver, como no podía ser de otro modo, con la ya mencionada clase <i>Dictionary</i>. Estoy harto de ver este fragmento de código en el software de cierta empresa imaginaria:</div><pre class="p"><b>if</b> (dict.ContainsKey(key))<br /> dict[key] = value;<br /><b>else</b><br /> dict.Add(key, value);</pre><div>Esto podría pasar como buena programación en <a href="http://es.wikipedia.org/wiki/Ankh-Morpork" target="_blank">Ankh-Morpork</a>, pero aquí solemos ser un poco más exigentes. Un buen día, uno de los leones, el método <i>Add</i>, se adelantó al gato y le comió la lengua al programador, cuando éste intentó insertar una clave duplicada en un diccionario. De ese día en adelante, el programador pincha primero el diccionario con una vara larga, para ver si ya existe la clave. En caso afirmativo, <i>actualiza</i> el valor asociado a la clave; de lo contrario, inserta el nuevo par clave/valor. El problema es que el mismo resultado se obtiene con menos charla:</div><pre class="p">dict[key] = value;</pre><div>En efecto, cuando se realiza una asignación sobre el indizador un diccionario, si exista la clave, se actualiza el valor; de lo contrario, se inserta. Hay dos posibilidades: o el programador lo sabía, y se le olvidó... o nunca se tomó la molestia de leerse la documentación. Prefiero pensar lo primero. Al fin y al cabo, la memoria viene genéticamente determinada, y uno no debe reñirle al prójimo por sus genes.</div><div>Por cierto, y si me permite el paréntesis, ¿sabía usted que podemos inicializar un diccionario genérico como si se tratase de una vulgar colección? ¡Es que un diccionario genérico es una colección! El quid está en saber qué tipo de colección: sus elementos son instancias de la clase genérica <i>KeyValuePar</i>. Esta es la técnica:</div><pre class="p"><b>private</b> <font color="darkcyan">Dictionary</font><<b>int</b>, <b>string</b>> d =<br /> <b>new</b> <font color="darkcyan">Dictionary</font><<b>int</b>, <b>string</b>>()<br /> {<br /> {1, "Uno"},<br /> {2, "Dos"},<br /> {3, "Tres"},<br /> };</pre><div>Preste atención a la coma, aparentemente superflua, que sigue al tercer par de valores. Se trata de un sencillo, pero poco conocido, truco sintáctico que permite añadir o quitar pares, en cualquier momento, sin preocuparnos por el carácter especial del último par. Este mismo truco se aplica a los inicializadores de objetos, e incluso a la declaración de tipos enumerativos. Si le parece una barbaridad y una concesión (hay gente para todo), recuerde que en C<sup>#</sup> el punto y coma es un terminador, no un separador. ¿Qué hay de malo en querer usar la coma también como terminador, en estos casos?</div><div>Vamos ahora a otra garantía que solemos olvidar... o simplemente desconocer. Esta vez, la garantía tiene que ver con el propio lenguaje:</div><pre class="p"><b>double</b> d = 0.0;<br /><b>if</b> (cond)<br /> d = 1.0;<br /><b>else</b><br /> d = 2.0;</pre><div>Naturalmente, la inicialización de la variable local sobra:</div><pre class="p"><b>double</b> d;<br /><b>if</b> (cond)<br /> d = 1.0;<br /><b>else</b><br /> d = 2.0;</pre><div>No pasa nada porque se deje <i>d</i> sin inicializar... aparentemente. La instrucción que sigue lo garantiza, sin importar cuál de sus ramas se ejecute. Por supuesto, en este caso sencillo, habría sido más fácil escribir todo el código de esta manera:</div><pre class="p"><b>double</b> d = cond ? 1.0 : 2.0;</pre><div>El programador que escribió el primer fragmento ha olvidado la sección de la especificación de C<sup>#</sup> que explica el concepto de <i>asignación definida</i>. La ha olvidado, o nunca se ha tomado la molestia de leerla.</div><div>Otro disparate frecuente debido al temor a lo desconocido:</div><pre class="p"><b>private double</b> f()<br />{<br /> <b>double</b> d = 0.0;<br /> OpenXXX();<br /> <b>try</b> {<br /> <b>if</b> (cond)<br /> d = f1();<br /> <b>else</b><br /> d = f2();<br /> }<br /> <b>finally</b> {<br /> CloseXXX();<br /> }<br /> <b>return</b> d;<br />}</pre><div>En realidad, el código anterior combina varios disparates debido al olvido o desconocimiento de las garantías de los contratos. Para empezar, vuelve a sobrarnos la inicialización de la variable local: la rama <b>try</b> del bloque <b>try</b>/<b>finally</b> nos garantiza la asignación definida de la variable, y la rama <b>finally</b> es irrelevante, si no utilizamos <i>d</i> en su interior. Tenga en cuenta que, si se ejecuta el <b>finally</b>, nunca llegaremos a la instrucción <b>return</b>.</div><div>Pero la zarpa que realmente aterra al programador del ejemplo es la posibilidad de ejecutar un <b>return</b> dentro de la rama <b>try</b>, por desconocimiento (u olvido) del funcionamiento de esta instrucción. Podemos ejecutar ese <b>return</b> sin problemas, y el código se simplifica entonces enormemente:</div><pre class="p"><b>private double</b> f()<br />{<br /> OpenXXX();<br /> <b>try</b> {<br /> <b>return</b> cond ? f1() : f2();<br /> }<br /> <b>finally</b> {<br /> CloseXXX();<br /> }<br />}</pre><div>El código simplificado es más corto, más comprensible y, probablemente, algo más eficiente, y ya no hablemos de la elegancia. ¿Moraleja? Conozca sus deberes y derechos, tal y como los especifican los contratos de su software, y así será mejor programador. O, mejor aún: <a href="http://es.wikipedia.org/wiki/RTFM" target="_blank">RTFM</a> (again).</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com10tag:blogger.com,1999:blog-22471446.post-3402180124279019852009-01-26T12:44:00.018+01:002009-01-26T18:50:12.209+01:00Más extraña que la ficción<div><img style="float:right; margin:1px 0 1px 8px; border-style:none; padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ9kdYFZetLkqvPtktd3tyOWurRwa4RxJqsJ9RRoZtGWAHAD14kkv79TEAlKuXwzygv4FJ0q_kcqPiixeT1hILHDELXLXfmAxpRkPB-d1Rv6LkPG86FPWdDR1GnjDj6fxkIypY/s320/witchdoc.jpg" border="0" alt="Si ya tienes un médico brujo, ¿para qué necesitas al Doctor Marteens?" /><span class="first">Había una vez</span> una empresa imaginaria, habitada por <a href="http://commanet.blogspot.com/2008/11/el-escarabajo-pelotero.html">escarabajos peloteros</a> que practicaban el <a href="http://commanet.blogspot.com/2008/04/cargocultismo_26.html">cargocultismo</a> mientras arrastraban sus fragantes bolitas de excrementos y esperaban el regreso de John Frumm. Un buen día, apareció un feo insecto en sus instalaciones, amenazando con devorar los cimientos corporativos. Para exterminarlo, los escarabajos ejecutaron la danza ritual del <a href="http://commanet.blogspot.com/2008/04/el-pollo-de-skinner.html">pollo de Skinner</a> y practicaron un exorcismo.</div><div>Demasiado folklore acumulado en un solo punto del espaciotiempo, en mi opinión. Un punto imaginario, quise decir...</div><hr class="p"><div style="font-size: 90%; font-style: italic; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">... y si enmascarar un bug mediante el simple expediente de unir dos instrucciones <b>try</b>/<b>catch</b> no es bailar la danza del pollo de Skinner, que vuelva John Frumm y lo vea.<br><br>Por supuesto, el problema real es que el nido del monstruo sigue estando ahí, no tan evidente como el dinosaurio de Monterroso, pero aún amenazante.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com0tag:blogger.com,1999:blog-22471446.post-63300516060344785162008-12-16T18:15:00.004+01:002008-12-16T18:36:49.588+01:00Libro sobre concurrencia<div><iframe src="http://rcm.amazon.com/e/cm?t=callindrmarte-20&o=1&p=8&l=as1&asins=032143482X&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr&nou=1" style="width:120px;height:240px; float:right; margin: 1px 0px 1px 8px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe><span class="first">Llevaba anunciándose desde</span> hace unos cuantos meses, y finalmente está disponible: <i>Concurrent programming on Windows</i>, de <a href="www.bluebytesoftware.com/blog">Joe Duffy</a>, quien, por cierto, nada tiene que ver con la <a href="http://www.youtube.com/watch?v=pp0ce1kS-yw" target="_blank">señorita</a> que canta aquello de <i>"You got me begging you for mercy"</i>...</div><div>El libro entra por derecho propio en la categoría <i>tocho</i>, en el buen sentido de la palabra. Si tiene un poco de cuidado con los testigos potenciales, puede arrojarlo desde una primera planta sobre cualquiera de sus enemigos. Asegúrese antes, sin embargo, de que el destinatario no lleva casco.</div><div>Observe que la sigla <i>.NET</i> no aparece en el título: en realidad, el libro trata sobre concurrencia utilizando el API nativo de Windows y también .NET. Esa es la buena noticia. La mala es que las partes que tratan sobre .NET suelen ir después de las disertaciones sobre el API nativo, y confieso que llego con sueño a la fiesta.</div><div>Si usted ya tiene experiencia con hilos y sincronización, puede que el libro no le enseñe muchas cosas nuevas: la culpa no es del autor, por supuesto, sino que a veces en realidad no hay "más allá". Me ocurría, en su momento, con InterBase: muchas personas me preguntaban por qué no escribía un libro sólo sobre InterBase... y la respuesta era y es que no había mucho de lo que escribir.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com8tag:blogger.com,1999:blog-22471446.post-52544015275200254292008-12-15T14:27:00.002+01:002008-12-15T14:32:44.987+01:00Task panels<div><span class="first">Por si a alguien</span> le hace falta, he subido un fichero a la sección de ejemplos de mi página con el código de un control para <i>paneles de tareas</i>:</div><a href="http://www.marteens.com/ejemplos.htm#taskpanels" target="_blank"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.marteens.com/images/taskpanels.gif" border="0" alt="TaskPanels" /></a><div>Haga clic sobre la imagen para ir a la <a href="http://www.marteens.com/ejemplos.htm#taskpanels" target="_blank">página de ejemplos</a>, donde encontrará las instrucciones pertinentes.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com0tag:blogger.com,1999:blog-22471446.post-82509869385183773772008-11-29T17:49:00.017+01:002008-12-03T12:58:45.244+01:00Consenting adults, consenting ducks<div><img style="float:right; margin:1px 0 1px 8px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmKrj3wGlZzfWys4GQrM303qqxdRuWw4y-tZw8eSPWRmt0BrXNgyLECcRAljmLafoPbnQj8hUc7WBXW3jN-Yai3cQ1gDdOhO2e6h_yCCe7V2mERG2Mbsz3vLDP-FVMjVcmfEGZ/s320/duckula.jpg" border="0" alt="No le toques los colmillos al Conde Duckula" /><span class="first">Hace poco, escribía en los</span> comentarios de otra entrada que esto la Informática no es una ciencia, sino una ingeniería... y Alfredo Novoa respondía que ni siquiera eso. La siguiente anécdota demuestra cuán cierta es, desgraciadamente, esta afirmación.</div><div>La historia viene a cuento de una característica "novedosa" llamada <i>duck typing</i>. Según sus defensores, si una clase tiene métodos <i>Add</i>, <i>Remove</i> e <i>Insert</i>, y un indexador, ¿qué hay de malo en asignar una instancia de la clase a una variable de tipo <i>ICollection</i>? Eso es, en pocas palabras, el <i>duck typing</i>: si algo camina como un pato, nada como un pato, vuela como un pato (es decir, las tres cosas las hace mal) y además caga como un pato (es decir, constantemente), entonces, según los adeptos del mecanismo, tiene que ser un pato. Naturalmente, yo digo que puede ser una oca, o un pato robot, o el conde Duckula. No es buena idea tratar al señor conde como a un vulgar pato. Y así lo hice ver aquí, aunque de pasada:</div><ul type="square"><li><a href="http://commanet.blogspot.com/2007/05/internal-affaires.html">Internal affaires</a></li></ul><div>Algún tiempo más tarde, alguien se quejó en un comentario... y como no suelo llevar cuenta de los comentarios tras cierto tiempo, no me percaté del hecho hasta hace un par de días. Esta es el comentario:</div><div style="font-size: 90%; font-style: italic; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">Cuál es el problema que le ves al "duck typing". Los que lo utilizan, suponen que los programadores son "consenting adults".</div><div><i>Consenting adults</i>, eh...</div><div>En principio, eso serviría también para justificar las conversiones de tipos bestiales que son el pan de cada día en C++. Al fin y al cabo, si yo sé que un puntero se representa como un entero de 32 bits en mi plataforma, ¿por qué no voy a poder incrementar ese entero en cuatro para acceder al siguiente elemento de un vector? <i>Consenting adults</i>. No estamos locos. Sabemos lo que queremos...</div><div>Ahora bien, ¿es tan peligroso el duck typing como las conversiones bestiales? Veo dos problemas principales:</div><ol><li>Una pila no es simplemente un objeto que tiene dos métodos <i>Push</i> y <i>Pop</i>. Esos dos métodos, además, tienen que satisfacer determinado contrato. En la práctica, si el pato es mío, es un descuido mío el que no haya hecho que la clase correspondiente implemente la interfaz deseada. Si el pato es ajeno, es muy peligroso asumir que puedo tratar a un águila como a un pato simplemente porque ambos tienen pico.</li><li>Si el pato es ajeno, además, los problemas con el versionado de clases pueden ser peliagudos. O "plumiagudos", si lo prefiere...</li></ol><div>En cualquier caso, me hace gracia el uso de la teoría de los <i>consenting adults</i>. Hace poco, en Alemania, un buen señor pidió a otro <i>consenting adult</i> que le cortase la picha y se la comiese (en ese orden). El otro, efectivamente, cumplió con lo pactado. Luego mató a la víctima, como ésta había solicitado, se comió un muslo, y puso el otro a secarse para hacer jamón. Al caníbal lo pillaron, y lo llevaron a juicio. E inmediatamente, se armó el correspondiente revuelo: ¿por qué condenarlo? ¿No se limitó a hacer lo que el otro pirado le pedía? ¿No eran una parejita de <i>consenting adults</i>?</div><div>Evidentemente, tengo mi propia opinión al respecto. Yo, al canibalizado, si lo pillase antes de ser devorado, no le haría nada. Al fin y al cabo, si quiere que otro lo mate y se lo zampe, es su problema. Es, en efecto, un <i>consenting adult</i>: se supone que sabe lo que hace. Mi problema es con el caníbal: es un pirado, y tengo serias sospechas de que intentará repetir la acción. La próxima vez, no estoy seguro de que vaya a encontrar un <i>consenting adult</i> para satisfacer sus antojos culinarios. Es un individuo peligroso. No sé si eso bastará (junto con lo que ya ha hecho) para enviarlo a la cárcel, pero en mi barrio no lo quiero.</div><div>Lo mismo se aplica a los <i>duck typers</i>. ¿De manera que usted es aficionado a transformar el agua en vino, perros en patos y patos en borricos? Ah, claro, usted sabe lo que hace. Pero lo que usted hace es peligroso. No me gustaría verle enredando con mis proyectos...</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com15tag:blogger.com,1999:blog-22471446.post-88270149166999951572008-11-28T00:20:00.004+01:002008-11-28T00:24:41.703+01:00¡No publique vectores!<div><img style="float:right; margin:1px 0px 1px 8px;border-style:none" src="http://www.marteens.com/nav/nav14.png" border="0" alt="" /><span class="first">Nueva píldora orientada</span> a objetos:</div><ul type="square"><li><a href="http://www.marteens.com/pills.html#dont_publish_arrays">Las propiedades o métodos que devuelven vectores son una mala idea</a></li></ul>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com2tag:blogger.com,1999:blog-22471446.post-79114760524596274162008-11-18T09:50:00.022+01:002008-11-18T14:44:51.927+01:00¡Te lo dije!<div><img style="float:right; margin:1px 0 1px 8px; border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjj4X_0jG1EgY02C-QCyaeyMJWu8QZj-wsOxxT1IMFiv_1t1WstqRf2XKrU_P-wNcQdfvO5e6KpjndClUwdFC0ryUz2G7qVmAHt6k1X7TzgUKGSsn7PIe9OM4amhri0dmrKjGCw/s320/beavis.jpg" border="0" alt="They never scored!" /><span class="first">Permítame que me eche</span> unas risas... ya, sí, ya puedo continuar...</div><div>Sin duda, usted conocerá a Beavis y su amiguete Butt-head. Son dos friquis metaleros de dibujos animados, que se pasaban la vida intentando "anotar"; meter un gol, ya me entiende... Cuando la serie llegó a su fin, el creador, jocosamente, escribió un epitafio para sus personajes:</div><div style="margin-left: 32px;">- They never scored.</div><div>Nunca "anotaron". Pobrecillos. Lo mismo, por desgracia, se puede decir del compilador de Borland para .NET: nunca llegó a marcar gol. Murió virgen aunque no casto.</div><div>¿Una tragedia? Nah. Tragedias son las guerras, el hambre, las enfermedades. Concedo que puede ser una molestia. Mi sabor favorito de Häagen-Dazs ya no se vende en mi barrio. Es molesto, e irritante, eso sí. De todos modos, tengo todo el derecho del mundo a decir: ¡TE LO DIJE!</div><h3>... y nada más que la verdad</h3><div>Veamos los hechos desnudos: un buen día, la compañía antes conocida como Inprise decide que su negocio de compiladores es una ruina. Segrega la compañía (igual que las babosas segregan un moquillo adherente), bautiza CodeGear al retoño, y anuncia que tiene un montón de novios. Pero la petición de mano se demora infinitamente y toda la villa comienza a sospechar que se trataba de un farol. Tras varias peripecias, aparece el Príncipe Azul, un tal <a href="http://www.embarcadero.com/" target="_blank">Embarcadero</a>, cabalgando radiante sobre su moto acuática, y carga con la solterona (¿le habrán pagado para que se la llevase de una puñetera vez?).</div><div>Uno esperaría que, tras tan accidentado romance, la parejita se pusiese a fabricar pequeños ogros, como Fiona y Shrek, pero... es que Fiona ya aportaba al enlace dos monstruitos: Delphi para Win32 y Delphi.NET. Ah, sí, y esa abominación de "Delphi para PHP" y otros engendros que mejor dejo sin clasificar. Bueno, menos trabajo para el ogro, ¿o no?</div><div>Entonces, la comedia se transforma en drama: Shrek tira Delphi.NET por la ventana y <a href="http://www.codegear.com/products/delphi/prism" target="_blank" title="Delphi Prism">toma en adopción</a> el brillante hijo de un vecino, llamado <a href="http://www.remobjects.com/oxygene.aspx" target="_blank">Oxygen</a>, aka Chrome. ¿Sabía Shrek lo que iba a hacer con su vástago desde el primer momento? ¿Se convenció a su pesar de que Delphi.NET no tenía arreglo? A todas estas, los representantes de Fiona parecen haberse puesto de acuerdo en mostrar su mejor sonrisa a los medios. Pero uno imagina que la procesión debe ir por dentro...</div><h3>El bien o el mal</h3><div>No me entienda mal: Oxygen es un producto que me gusta y que recomiendo sin reservas. De hecho, en cierta manera lo considero un familiar cercano. <a href="http://freyalang.blogspot.com" target="_blank">Freya</a> ha copiado impúdicamente algunas ideas de Oxygen/Chrome, como la palabra clave <b>method</b> o los niveles de visibilidad. Muchas características comunes son fruto de una evolución paralela: es un indicio de que ambos proyectos arribaron a los conclusiones lógicas en esos puntos. A la vez, me empeño en creer voluntariosamente que puede que alguna característica de Freya se haya podido filtrar a Oxygen/Chrome.</div><div>¿Es el párrafo anterior una recomendación de compra de Delphi Prism? ¡Más despacio, por favor! Si usted odia orgánicamente las llaves, le recomendaría que comprase Oxygen. Delphi Prism es Oxygen más toda la morralla de "compatibilidad" con los obsoletos módulos heredados de CodeGear. ¿Alguien en sus cabales se atreve a programar con dbExpress.NET... cuando dbExpress nativo terminó siendo un fracaso sonado? Si va a programar con Oxygen, mi recomendación es que utilice ADO.NET, o los productos de acceso a datos de <a href="http://www.remobjects.com/">RemObjects</a>. Cualquier otra decisión, significaría empantanarse por puro capricho.</div><div>En cualquier caso, Embarcadero tiene un bonito problema: Delphi Prism y Delphi for Win32 son dos lenguajes muy diferentes. ¿Tendrá secuela este peliculón?</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com47tag:blogger.com,1999:blog-22471446.post-8899461116226598722008-11-17T09:28:00.018+01:002008-11-17T11:01:11.778+01:00Genericidad en vez de typeof<div><img style="float:right; margin:1px 0 1px 8px;border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkG7OKlFrBEeJMMltbQdbT8cjf0vlBy4fKQ0Fq9bX7XLb2Go-J54JMRqFMpsBzDhwGhEPTjrh5e0BqGMfh4A82OorJJiIEpSVj_N-B5tjp8Zmt59Ys-tEsXvtM9QMZqdSYgDAq/s320/cube.jpg" border="0" alt="" /><span class="first">Permítame un</span> breve descanso entre tantos disparates. He estado usando últimamente una técnica sencilla y conocida para identificar posibilidades de simplificación en código heredado de .NET v1.1. La técnica consiste en buscar métodos, en el código fuente, que reciban un parámetro de tipo <i>System.Type</i>, para considerar la posibilidad de convertir dicho método en un método genérico.</div><div>El caso típico se encuentra casi siempre en el "gestor" de ventanas:</div><pre class="p"><b>public interface</b> <font color="darkcyan">IFormManager</font><br />{<br /> <font color="darkcyan">Form</font> ShowForm(<font color="darkcyan">Type</font> formType);<br />}</pre><div><i>ShowForm</i> recibe un tipo de ventana como parámetro, y debe encontrar una ventana abierta cuyo tipo sea exactamente el especificado. Si la encuentra, debe activarla y pasarla a primer plano. En caso contrario, debe crear una ventana de este tipo, y mostrarla. La regla que he mencionado establece que el método anterior debería transformarse en algo parecido a lo siguiente:</div><pre class="p"><b>public interface</b> <font color="darkcyan">IFormManager</font><br />{<br /> T ShowForm<T>()<br /> <b>where</b> T: <font color="darkcyan">Form</font>, <b>new</b>();<br />}</pre><div>Puntos a destacar:</div><ol><li>El parámetro de tipo <i>T</i> se ha restringido de dos maneras. En primer lugar, debe ser una clase descendiente de <i>Form</i>. Usted, naturalmente, puede exigir de sus ventanas que desciendan de alguna clase más concreta, o que implementen algún tipo de interfaz general, o ambas cosas a la vez.</li><li>Por otra parte, he exigido que las ventanas tengan un constructor sin parámetros. Este es un requisito opcional, que no valdrá en todos los casos, pero que he incluido aquí para simplificar un poco el código.</li><li>Observe que será imposible, al utilizar este método, que el compilador pueda "inferir" el parámetro de tipo. Si no ha trabajado aún con la inferencia de tipos en llamadas a métodos genéricos, traduzca la oración anterior así: <i>"siempre tendremos que indicar explícitamente el tipo de ventana"</i> (algo bastante razonable).</li></ol><div>¿Qué hemos ganado con esta transformación? Antes, para mostrar una ventana, teníamos que escribir código como el siguiente:</div><pre class="p"><i><font color="green">// "manager" implementa "IFormManager"</font></i><br /><font color="darkcyan">MiVentana</font> v = manager.ShowForm(<br /> <b>typeof</b>(<font color="darkcyan">MiVentana</font>)) <b>as</b> <font color="darkcyan">MiVentana</font>;</pre><div>Si no efectuamos la conversión de tipos, obtendremos como resultado un valor de tipo <i>Form</i>, sin más adornos. En cambio, con la versión genérica obtendremos el tipo originalmente deseado:</div><pre class="p"><font color="darkcyan">MiVentana</font> v = manager.ShowForm<<font color="darkcyan">MiVentana</font>>();</pre><div>Para cerrar el círculo, le mostraré una sencilla implementación inicial del tipo de interfaz:</div><pre class="p"><b>public class</b> <font color="darkcyan">FormManager</font> : <font color="darkcyan">IFormManager</font><br />{<br /> <b>private</b> <font color="darkcyan">List</font><<font color="darkcyan">Form</font>> forms = <b>new</b> <font color="darkcyan">List</font><<font color="darkcyan">Form</font>>();<br /> <br /> <b>public</b> T ShowForm<T>()<br /> <b>where</b> T: <font color="darkcyan">Form</font>, <b>new</b>()<br /> {<br /> <b>foreach</b> (<b>var</b> f <b>in</b> forms)<br /> <b>if</b> (f.GetType() == <b>typeof</b>(T))<br /> {<br /> f.Show();<br /> f.BringToFront();<br /> <b>return</b> f <b>as</b> T;<br /> }<br /> <b>var</b> frm = <b>new</b> T() { Visible = <b>true</b> };<br /> frm.FormClosed += (sender, e) =><br /> forms.Remove((<font color="darkcyan">Form</font>)sender);<br /> forms.Add(frm);<br /> <b>return</b> frm;<br /> }<br />}</pre><div>Creo que la primera parte, la que ejecuta el bucle, es fácil de comprender. Luego he complicado un poco las cosas: observe que puedo ejecutar el constructor sin parámetros del tipo <i>T</i> gracias a las restricciones establecidas sobre el parámetro genérico. Si mis ventanas no se pudiesen construir de esa manera, sino que requiriesen parámetros, entonces tendría que utilizar un poco de reflexión. Tenga presente, no obstante, que la ejecución de un constructor sin parámetros sobre un parámetro de tipo genérico es implementada por el compilador como una llamada al API de reflexión (lo sé porque Freya hace exactamente lo mismo).</div><div>¿Ve esas llaves tras la llamada al constructor? Se trata simplemente de una asignación a la propiedad <i>Visible</i> del formulario recién creado. Podía haberla sustituido por una llamada al método <i>Show</i>, pero quise mostrar un ejemplo de inicialización de objetos. Luego, resolvemos un problema más serio: <i>FormManager</i> lleva una lista de las ventanas abiertas (o abiertas e invisibles). Las ventanas se añaden al ser creadas, pero ¿cuándo se eliminan de esta lista? Para ello, asociamos un manejador al evento <i>FormClosing</i> de cada nueva ventana. En vez de crear el manejador como un método independiente, he preferido utilizar una expresión lambda.</div><hr class="p"><div style="font-size: 90%; font-style: italic; margin-left:24px; margin-right:24px; background-color: #c0c0ff; padding: 4px 8px 4px 8px; line-height:105%;">Comentarios adicionales: ¿para qué hace falta una interfaz IFormManager? ¿No sería más sencillo diseñar ShowForm como un método estático, dentro de una clase estática? La clave del uso de IFormManager está en la posibilidad de usar diferentes implementaciones según nos convenga. La manía de recurrir a métodos estáticos a la primera es una manifestación de lo que yo llamo "la fiebre del singleton". Es cierto que, al final, nuestro gestor de ventana tendrá que proveer algún singleton, como la variable "manager" de mi ejemplo de uso. Pero la aparición del singleton debe retrasarse todo lo que podamos.</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com5tag:blogger.com,1999:blog-22471446.post-77821964812082035912008-11-12T23:13:00.015+01:002008-11-17T00:46:51.759+01:00Antología del Disparate - III<div style="font-style:italic;text-align:right;">Advertencia: Cualquier parecido con<br />la coincidencia, es sólo una sospechosa irrealidad...</div><div><img style="float:right; margin:1px 0 1px 6px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihtO_I4po-b4vk15l7LnT1XNXVMbosEGwT27rlXd_FguEsN65ad4LU7hiPsku8IWIVfH2WK2Y7bghPOcAXSPg-CaO6zFGD3UPwKmpxCZHohQCXKQifjMgan3obJMWbYakKI5L9/s320/skid.jpg" border="0" alt="Primero uno resbala..." /><span class="first"><img style="float:left; margin:4px 2px 0px 0px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMcvoA5RvDI16nKgiO8BKDWqASCH-aPUXNUq-twZgfbgUHHeoVRGvrg6_76Qc_VayooN1bNd0W8Td9Uz_Xd9qtIK8j9g4ZasB-HTO6-sPD4mJlMWLtSKeHKJaMdG8fTWBnON0x/s320/cap_c.png" border="0" alt="" />uando, meses atrás,</span> se me ocurrió la idea de escribir sobre el <a href="http://commanet.blogspot.com/2008/04/cargocultismo_26.html" target="_blank">cargocultismo</a> estaba pensando, en particular, en el problema artificial, creado por mis amigos imaginarios, que voy a discutir a continuación. Repasemos la definición: el <i>cargocultista</i> es el individuo que practica determinados rituales técnicos porque sabe, o ha oído decir, que a otros les funcionan, pero sin comprender realmente cómo se supone que tienen que funcionar.</div><div>En el mundo de la programación, uno de esos rituales misteriosos tiene que ver con la creación de <i>gestores</i> especiales para distintos módulos de la aplicación, sobre todo cuando se trata de proyectos grandes. Una aplicación "decente" tiene que definir, por fuerza, un "gestor" de menúes, un "gestor" de excepciones, un "gestor" de formularios... y así hasta el aburrimiento o la bancarrota. Para ser exactos, una parte importante de esta secta de cargocultistas no utiliza la palabra "gestor" sino <i>manager</i>, que se supone que debe darle un poco más de dignidad al engendro.</div><div><img style="float:right; margin:1px 0 1px 6px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKxW7Foa_RQATWaKK1b-ErG_0GBB-qhhxW7WC0ERW3bM0mP65APMyfeimZiZFz3_zlrEy_iwj1glDSqDJBkOSbuxcKCYmZ1mVdfUl5inI-3FCA4hS2XZa42P9fbV7Qg2h3YR2b/s320/water.jpg" border="0" alt="... y luego, si no tienes cuidado, te la pegas." />Como suele ocurrir en todos los casos de cargocultismo, la propia idea de un gestor de menúes y ventanas no es mala. ¡Todo lo contrario! Estos módulos suelen ahorrar mucho código repetitivo, y tienen la bonita propiedad de dejarse reutilizar fácilmente de un proyecto al siguiente. Incluso cuando se utilizan en un único proyecto, nos ofrecen la libertad de hacer cambios notables en la interfaz de usuario sin necesidad de retocar todo el código. Visual Studio, por ejemplo, nos permite elegir entre una aplicación MDI tradicional o una en la que los documentos aparecen en páginas paralelas accesibles por pestañas. Lo verdaderamente malo, es inventarse uno de estos gestores sin saber por qué, para qué o cómo.</div><h3>Cuello de botella</h3><div>A diferencia de disparates anteriores, esta vez el problema no está en una simple instrucción equivocada, sino en el diseño global de un módulo. Los problemas comienzan con la decisión, consciente o inconsciente, de esta gente de ignorar el mecanismo preestablecido para la traducción en .NET y de montar un barroco sistema basado en ficheros XML externos. El vínculo entre cada elemento de la interfaz y su correspondiente cadena se realiza manualmente. Supongamos que usted quiere añadir una etiqueta a un formulario que debe decir <i>"Total"</i>. De acuerdo a la metodología de estos señores:</div><ol><li>Primero debe ocurrírsele un nombre global para identificar la etiqueta. No se les ocurrió generar ese nombre a partir del nombre del control y del formulario donde se encuentra.</li><li>Luego tiene que ir a cada fichero XML de traducción y crear una entrada que traduzca el identificador al correspondiente idioma.</li><li>Cada formulario tiene un método <i>Traducir</i>. No crea que se trata de un método virtual heredado: la herencia visual es algo desconocido en esta metodología. Tiene que añadir una instrucción manualmente a <i>Traducir</i> en la que asignará a la propiedad <i>Text</i> de la etiqueta, el resultado de una llamada explícita a un método que busca en un diccionario en memoria la traducción correspondiente.</li></ol><div>Usted se dirá que... bueno, es bastante trabajo, claro... pero con un poco de disciplina... etc, etc. ¡Ah, cuán equivocado estaría! ¿No le he dicho aún que esta gente trabajaba con SourceSafe, para el control de versiones? Imagine lo que ocurre cada vez que dos programadores intentan programar dos formularios que nada tienen que ver entre sí: ¡se tienen que pegar por el acceso a los ficheros de idiomas!</div><div>De todas maneras, aunque no se produjese este increíble cuello de botella, el sistema es inaceptable:</div><ol><li>Microsoft ofrece, de serie y sin coste adicional, un mecanismo idéntico en funcionalidad y sin ninguno de estos problemas: ficheros de recursos y ensamblados satélites.</li><li>El sistema de Microsoft no le obliga a añadir código manualmente en ningún momento.</li><li>El sistema de Microsoft le permitiría, incluso, ahorrar trabajo suponiendo la existencia de un "idioma neutro", para el cuál no tendría que mantener una tabla de recursos adicional.</li><li>El sistema de Microsoft es verificable en tiempo de compilación: si compila, funcionará. En cambio, el invento en cuestión utiliza cadenas como claves en el diccionario. Resulta imposible verificar automáticamente la existencia de errores de mecanografía. Por supuesto, si esta gente hubiese usado enumerativos para las claves, el problema se resolvería... pero los dioses ciegan a aquellos a quienes quieren destruir.</li></ol><div>Moraleja parcial: no hay que reinventar la rueda, si además, va a rodar más despacio.</div><h3>Escarnio de la locura</h3><div>Pues bien, queridos amiguitos, hay también un menú diseñado de acuerdo a esta locura con sistema. Hay claves con formato de cadenas, que tienen que transmitirse desde el código al fichero externo y viceversa, hay versiones del menú para cada idioma soportado y todo lo demás. Pero me voy a centrar en un aspecto muy concreto: en cómo se maneja la ejecución de los comandos del menú.</div><div>Cada vez que se ejecuta un comando del menú principal, se invoca un manejador de eventos situado en la ventana principal. No crea que hay un manejador diferente para cada comando, lo cuál habría sido relativamente sencillo de conseguir: todos los comandos van a parar al mismo método. Y ese método, cómo no, contiene una inmensa instrucción <b>switch</b>, que toma la cadena de caracteres designada como clave del comando para ejecutar el código apropiado.</div><div>¿Y en qué consiste ese "código apropiado"? Maravíllese: en una llamada a un método global estático de una clase independiente, a la que llamaremos <i>Acciones</i>, que se encuentra en un gigantesco fichero C<sup>#</sup>.</div><ul type="disc"><li>Nuevamente, tenemos un fichero que se convierte en un cuello de botella.</li></ul><div>Claro, los conflictos por el acceso al fichero de acciones sólo tendrían que surgir cuando se modificase la estructura del menú... ¿o no? Resulta que no: cuando esta gente tiene que mostrar una ventana que permite una sola instancia, incluye, en el fichero de acciones, un bucle explícito para buscar si existe ya una instancia del tipo de ventanas en una lista global. Si la encuentran, la activan. Y si no, la crean. Y este código se repite tropecientas y tantas veces, una por cada ventana de este tipo, en el fichero de acciones.</div><ul type="disc"><li>Con un poco de reflexión, el código común se podría haber reducido a una simple llamada.</li><li>Pero mejor aún, el <b>switch</b> original ni siquiera tendría que haber pasado por la clase <i>Acciones</i>, sino que tendría que llamar directamente a métodos estáticos de las clases de ventanas correspondientes. De esa manera, cualquier cambio en la forma de mostrar una ventana, se aislaría dentro del código de la propia ventana.</li><li>Para rematar, esta gente ni siquiera utilizó una metodología uniforme para la creación y activación. A veces llamaban a <i>Show</i>, otras veces combinaban <i>BringToFront</i>, <i>Activate</i> y <i>Focus</i>; a veces cambiaban la forma del cursor, y en otros casos, mostraban una ventana <i>splash</i> con el mensaje <i>"Espere un poco..."</i>. Esto complica mucho limpiar el código.</li></ul><div>Con todo, lo peor es que a estar alturas todavía les cuesta creer que este sistema sea un error: el cargocultismo susurró en sus oídos que necesitaban un "gestor de menúes" para su aplicación "seria", y ahora tiemblan cada vez que mis tijeras podan porciones cada vez mayores de este cadáver exquisito. Mientras limpio los fluídos putrefactos de mis instrumentos de corte, veo ojos asustados y oigo el coro de plañideras a mi alrededor. Sonrío tranquilizadoramente, pero compruebo que mi sonrisa parece cada día más siniestra.</div><div>Y es que uno termina hartándose, ¿sabe?</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com11tag:blogger.com,1999:blog-22471446.post-31373236431221240442008-11-11T00:03:00.013+01:002008-11-11T00:23:15.034+01:00Antología del Disparate - II<div style="font-style:italic;text-align:right;">Advertencia: Cualquier parecido con<br />la realidad, es sólo una sospechosa coincidencia...</div><div><span class="first"><img style="float:left; margin:4px 2px 0px 0px;border-style:none;padding:0px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDBrNI09tizPlw91-sCTS8gzxECwUxOR-jjf5MBJxQoE0t8CEY77BPqt-YLwKk9GPTLhD4X2QLNNB_5Pvi4pG9zuilPXeRObuB1nAb44XLB_dTUTwM3vX95mNU1AUO9Lti2dek/s320/cap_e.png" border="0" alt="" /><img style="float:right; margin:1px 0 1px 8px;border-style:none;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIbxAMurBcQvqo1FuzRTx7POebWbuS-18QPleWTMa8g-He0iiU1tuTgT7S9hMgfTCw-90n7SaXtt-ptD07kIFucQBYxwexpok49uyaOQIPruBqey66HIvcr53Fg17ULdiEceWG/s320/boxing.png" border="0" alt="Boxing" />stamos otra vez en</span> una aplicación que maneja un buen puñado de hilos. El "genio" que la programa ya ha tomado consciencia de los peligros de la falta de sincronización, y dedica su tiempo libre a repartir bloqueos a diestra y siniestra. Este es un bello ejemplo de cómo se las gasta el caballerete (imaginario, <i>of course</i>):</div><pre class="p"><b>private bool</b> flag;<br /> <br /><b>public void</b> YoQueSe()<br />{<br /> <b>if</b> (<font color="darkcyan">Monitor</font>.TryEnter(flag))<br /> {<br /> <i>// Código "sincronizado" ...</i><br /> <font color="darkcyan">Monitor</font>.Exit(flag);<br /> }<br />}</pre><div>¿Puede ver los disparates? Son varios, por supuesto. ¿Cómo cree que se manifestó el problema? ¿Se atreve a imaginar cómo se le pudo haber ocurrido esta barbaridad a su perpetrador?</div><hr class="p"><div>Esta es, amigo mío, la declaración del método <i>TryEnter</i> de la clase <i>Monitor</i>:</div><pre class="p"><b>public static bool</b> TryEnter(<b>object</b> obj);</pre><div>Como puede ver, el parámetro del método debe ser un <i>Object</i>, o más exactamente, una referencia a un objeto. Como <i>Object</i> es la clase base final del CLR, cualquier cosa que pasemos como parámetro servirá, a efectos del sistema de tipos. El método incluso admite que pasemos un valor de tipo <i>Boolean</i>...</div><div>... con la única pega de que <i>Boolean</i> es una estructura, un tipo con semántica de asignación por valor. Para poder pasar un <i>Boolean</i> donde se espera un objeto, .NET aplica la operación llamada <i>boxing</i> al valor. Es decir, crea un objeto temporal del tamaño adecuado, y copia en su interior el valor que queríamos pasar. ¿No queríamos una <i>referencia</i>? Tras el <i>boxing</i> ya tenemos una.</div><div>¿Y qué hay con la llamada final a <i>Exit</i>? Este es el prototipo de este segundo método:</div><pre class="p"><b>public static void</b> Exit(<b>object</b> obj);</pre><div>Nuevamente, nos piden un objeto. Y el compilador no protesta si pasamos un valor de tipo <i>Boolean</i>. Simplemente mete el valor en una "cajita" y pasa el puntero a ésta. ¿La pena? Pues que las llamadas a <i>TryEnter</i> y <i>Exit</i> deberían utilizar <b>el mismo objeto</b>... y cada vez que se realiza el <i>boxing</i>, ¡se crea una instancia temporal diferente!</div><div>¿Qué consecuencias cree que tenía el disparate? Sorprendentemente, ¡ninguna!... al menos, mientras la aplicación se mantuvo ejecutando sobre .NET v1.1. Fue al migrar a .NET 3.5 que estos errores empezaron a ser detectados <a href="http://msdn.microsoft.com/en-us/magazine/cc163606.aspx" target="_blank">en tiempo de depuración</a>. La llamada a <i>Exit</i> comenzó a generar una excepción, advirtiendo que se intentaba desbloquear a través de un objeto no bloqueado. Claro, sería una estupidez que el CLR tuviese que verificar en cada llamada a <i>Exit</i> que el objeto pasado no sea una instancia creada por <i>boxing</i>: penalizaría a los buenos programadores para evitar que los peores metiesen la pata.</div><div>¿Se da cuenta de que, ni siquiera en .NET v1.1, la aplicación estaba protegiendo la sección de código de marras? Si en algún momento se produjeron errores de sincronización en dicha sección, o fueron ignorados... o se perdieron gracias a la demencial política de manejos de excepciones de mi aplicación imaginaria. Observe que hay un segundo error en el código mostrado: se supone que las llamadas a <i>TryEnter</i> y <i>Exit</i> deberían estar asociadas a una instrucción <b>try</b>/<b>finally</b>.</div><div>Y ahora viene lo mejor: ¿cómo imagina que se le ocurrió la burrada al genio? El uso de <i>TryEnter</i> nos permite reconstruir los hechos. En realidad, el aprendiz de brujo intentó usar la instrucción <b>lock</b> en primer lugar. ¡Pero <b>lock</b> sí que puede detectar cuando alguien le pasa una estructura en vez de una referencia! Y como <b>lock</b> no se dejaba violentar, el muy pillo tradujo el código que suponía que se generaba para dicha instrucción. Pero como manazas que es, incluso eso lo hizo mal...</div><div>Moraleja: odio repetirme pero, por favor, esos genios que deambulan por ahí, ¡<a href="http://es.wikipedia.org/wiki/RTFM" target="_blank">RTFM</a>!</div>Ian Marteenshttp://www.blogger.com/profile/01802552330071620377noreply@blogger.com7