Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta programación. Mostrar todas las entradas
lunes, 10 de diciembre de 2007
Hasta la versión 3.0 de C#, la declaración de una variable se debía realizar indicando su tipo de datos antes del identificador elegido para la misma. También era muy frecuente definir en ese mismo momento su valor inicial, siguiendo un patrón similar al siguiente:
  string s = "cadena";
Sin embargo, la declaración anterior es redundante. Si la constante "cadena" es un string, ¿por qué hace falta indicar que la variable s también lo es?

Las variables locales implícitamente tipadas permiten obviar en su declaración el tipo que tendrán, dejando al compilador la tarea de averiguar cuál será en función de las variables o constantes que se usen al inicializarlo. Por tanto, será posible escribir código como:
  var x = XmlDateTimeSerializationMode.Local;
Y será equivalente a:

XmlDateTimeSerializationMode x = XmlDateTimeSerializationMode.Local;
 
Creo que no hace falta decir cuál de ellas es más cómoda a la hora de programar, ¿no? Simplemente hemos indicado con la palabra var que preferimos que sea el compilador el que haga el trabajo sucio.

Otro contexto donde este tipo de variables pueden facilitarnos la vida de forma frecuente es en los bucles for, foreach y bloques using:
  // Un ejemplo de bucle...
var chars = "Saludicos".ToCharArray();
foreach (var ch in chars)
{
Console.Write(ch); // ch es char
}

// Y ahora un bloque using...
using (var ctx = new AppContext())
{
// usamos ctx, que es de tipo AppContext
}
 
También es importante la existencia de esta nueva forma de declaración para posibilitar la creación de objetos de tipo anónimo, es decir, aquellos cuya especificación de clase se crea en el mismo momento de su definición. Por tanto, una declaración como la siguiente será válida:
  var x = new { Nombre = "Juan", Edad = 23 };
Para no desviarme del objetivo de este post, otro día hablaremos más detenidamente de las clases anónimas, aunque adelantaré que una vez compilado el código anterior el resultado será algo parecido al siguiente:

class __Anonymous1
{
private string nombre ;
private int edad ;
public string Nombre { get { return nombre ; } }
public int Edad { get { return edad ; } }
public __Anonymous1(string nombre, int edad)
{
this.nombre = nombre;
this.edad = edad;
}
}
...
__Anonymous1 x = new __Anonymous1("Juan", 23);

 
Existen ciertas reglas de obligado cumplimiento para usar variables locales implícitamente tipadas:
  • La declaración debe incluir un valor de inicialización. En otras palabras, no será posible usar en una línea var i;, puesto que el compilador no podría inferir el tipo en este momento.
  • El valor de inicialización debe ser una expresión evaluable como clase en tiempo de compilación. Expresamente prohibido el valor null, es decir, nada de var n = null;, puesto que el compilador no sabría de qué tipo se trata; eso sí, si fuera necesario, podría forzarse un casting var str = null as string;.
  • La declaración no puede incluir más de una variable. Una línea como var i = 1, s = "hola"; generará un error en compilación.
  • El inicializador no puede referirse a la propia variable declarada, obviamente.
  • Sólo pueden utilizarse como variables locales en bloques de código, en bucles for y foreach y como recurso de un bloque using.

Por último, me gustaría añadir un par de detalles que considero interesantes.

Seguimos teniendo Intellisense...Primero, el hecho de utilizar la palabra "var" y no indicar de forma explícita el tipo puede hacernos pensar que estamos utilizando tipado dinámico en tiempo de ejecución, como lo hacemos con Javascript, sin embargo esto no es así. Como he comentado anteriormente, el compilador toma el tipo del lado derecho de la asignación de inicialización, por lo que si la variable no es inicializada se produce un error de compilación:
Implicitly-typed local variables must be initialized
(Las variables locales implícitamente tipadas deben ser inicializadas)
Por ello, un entorno como Visual Studio sabe en todo momento el tipo exacto de que se trata, y nos puede ofrecer las ayudas en la codificación, detección de errores sintáticos e intellisense, como se puede observar en la imagen.

Segundo, y muy interesante. Como ya comenté en su momento, la nueva versión de Visual Studio permite la generación de ensamblados para versiones anteriores de la plataforma, es decir, que es posible escribir código C# 3.0 y generar un ensamblado para el framework 2.0. Esto, a efectos prácticos, implica que una vez demos el salto definitivo a VS2008 podremos usar las variables locales de tipo implícito, así como otras novedades del lenguaje, incluso en aplicaciones diseñadas para .NET 2.0.



Publicado en: Variable Not Found.
lunes, 3 de diciembre de 2007
Hace unos días publicaba una entrada donde hablaba de los problemas que generan la inclusión y el mantenimiento de comentarios en el código fuente de nuestras aplicaciones, aunque para no extenderme mucho sólo cité brevemente algunos aspectos a tener en cuenta a la hora de afrontar estos inconvenientes.

Ahora, partiendo de estos consejos, la abundante literatura que hay sobre el tema y mi propia experiencia, he creado los 13 consejos para comentar tu código, que contribuirán a hacerlo más inteligible y por tanto a incrementar su mantenibilidad a lo largo del tiempo.

1. Comenta a varios niveles

Comenta los distintos bloques de los que se compone tu código, aplicando un criterio uniforme y distinto para cada nivel. Puedes, por ejemplo, seguir un modelo como:
  • En cada clase, incluir una breve descripción, su autor y fecha de última modificación
  • Por cada método, una descripción de su objeto y funcionalidades, así como de los parámetros y resultados obtenidos
En realidad, lo importante es ceñirse a unas normas (comúnmente aceptadas si se trata de trabajo en equipo) y aplicarlas siempre. Las reglas concretas pueden ser elegidas a la conveniencia de cada cual.

Obviamente, una solución bastante aceptable e incluso aconsejable es utilizar las convenciones y herramientas (como XML en C# ó Javadoc para el mundo Java) que ayudan y facilitan esta tarea.

2. Usa párrafos comentados

Como complemento al punto anterior, es recomendable dividir un bloque de código extenso en "párrafos" que realicen una tarea simple, e introducir un comentario al principio de forma que se guíe al lector, precediéndolos, además, de una línea en blanco que ayude a separar cada uno del anterior.
  ...


// Comprobamos si todos los datos
// son correctos
foreach (Record record in records)
{
if (rec.checkStatus()==Status.OK)
{
...
}
}


// Ahora pasamos a realizar las
// transacciones
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
...

3. Tabula por igual los comentarios de líneas consecutivas

Si tenemos un bloque de líneas de código donde existe por cada una de ellas un comentario, es buena costumbre tabularlos todos a la misma posición, de forma que quedarán alineados y serán más sencillos de leer, sobre todo si forman parte de la misma frase.
  const MAX_ITEMS = 10; // Número máximo de paquetes
const MASK = 0x1F; // Máscara de bits TCP
 
Ojo a las tabulaciones. Hay editores que usan el carácter ASCII (9) y otros, en cambio, lo sustituyen por un número determinado de espacios, que suelen variar según las preferencias personales del desarrollador. Lo mejor es usar espacios simples o asegurarse de que esto es lo que hace el IDE correspondiente.

4. No insultes la inteligencia del lector

Debemos evitar comentarios absurdos como:
   if (a == 5)     // Si a vale cinco, ...
counter = 0; // ... ponemos el contador a cero
...
 
Este exceso necesita mucho tiempo a la hora de su creación, lo necesitará para su mantenimiento y, además, la mayoría de las veces distraerá al lector con detalles que no es necesario conocer o que pueden ser deducidos echando un vistazo al código.

5. Sé correcto

Evita comentarios del tipo "ahora compruebo que el estúpido usuario no haya introducido un número negativo", o "este parche corrije el efecto colateral producido por la patética implementación del inepto desarrollador inicial".

El uso de este tipo de comentarios no dice nada a favor de su creador, y, además, nunca se sabe quién los va a leer en el futuro. Emarts, en "Sapos, culebras y código fuente" muestra ejemplos de comentarios de este tipo.

Otro tema relacionado y, a mi entender, igualmente importante: cuida la ortografía. El hecho de que los comentarios no se vean desde el exterior no implican que puedas descuidarlos. Una ortografía correcta mejora la calidad de la expresión escrita y, por tanto, de la comunicación, que es de lo que se trata.

6. No pierdas el tiempo

No comentes si no es necesario, no escribas nada más que lo que necesites para transmitir la idea. Nada de diseños realizados a base de caracteres ASCII, ni florituras, ni chistes, ni poesías, ni chascarrillos.

En resumen, mantén los comentarios simples y directos, pues de lo contrario harás perder tiempo a tu sucesor. Para entender el efecto negativo de una verborrea excesiva, no hay como echar un vistazo a Hyperverbosity, publicado en Worse Than Failure hace unos días.

7. Utiliza un estilo consistente

Hay quien opina que los comentarios deberían ser escritos para que los entendieran no programadores. Otros, en cambio, piensan que debe servir de ayuda para desarrolladores exclusivamente.

En cualquier caso, coincidiendo con Ryan Campbell en su post Successful Strategies for Commenting Code, lo que importa es que siempre sea de la misma forma, orientados al mismo destinatario. Personalmente, dudo mucho que alguien de un perfil alejado a la programación vaya a acercarse al código, por lo que, para mi gusto, bastaría con cubrir el segundo caso de los expuestos anteriormente.

8. Para los comentarios internos usa marcas especiales


Y sobre todo, aunque no únicamente, cuando se trabaja en un equipo de programación en el que varias personas pueden estar tocando las mismas porciones de código. El ejemplo típico es el comentario TODO (to-do, por hacer), que describe funciones pendientes de implementar:
  int calcula(int x, int y)
{
// TODO: implementar los cálculos
return 0;
}
 
En este caso los comentarios no se usan para explicar una porción de código, sino para realizar anotaciones sobre el mismo a las que hay que prestar especial atención. Eso sí, si usas esta técnica, recuerda que debes actualizarlos conforme las anotaciones dejen de tener sentido.

9. Comenta mientras programas

Ve introduciendo los comentarios conforme vas codificando. No lo dejes para el final, puesto que entonces te costará más de el doble de tiempo, si es que llegas a hacerlo. Olvida las posturas "no tengo tiempo de comentar, voy muy apurado", "el proyecto va muy retrasado"... son simplemente excusas. Si tu intención es comentar el código, no te engañes y ¡hazlo!

Hay incluso quien opina que los comentarios que describen un bloque deberían escribirse antes de codificarlo, de forma que, en primer lugar, sirvan como referencia para saber qué es lo que hay que hacer y, segundo, que una vez codificado éstos queden como comentarios para la posteridad. Un ejemplo:

public void ProcesaPedido()
{
// Comprobar que hay material
// Comprobar que el cliente es válido
// Enviar la orden a almacén
// Generar factura
}

La codificación de cada una de las tareas descritas en el lenguaje correspondiente se realizaría justo debajo del comentario, quedando éste como encabezado de párrafo (como se describe en el consejo 2).

10. Comenta como si fuera para tí mismo. De hecho, lo es.

A la hora de comentar no pienses sólo en mantenimiento posterior, ni creas que es un regalo que dejas para la posteridad del que sólo obtendrá beneficios el desarrollador que en el futuro sea designado para corregir o mantener tu código.

En palabras del genial Phil Haack,
"tan pronto como una línea de código sale de la pantalla y volvemos a ella, estamos en modo mantenimiento de la misma"

Como consecuencia, nosotros mismos seremos los primeros beneficiaros (o víctimas) de nuestro buen (o mal) hacer.

11. Actualiza los comentarios a la vez que el código

De nada sirve comentar correctamente una porción de código si en cuanto éste es modificado no se actualizan también los comentarios. Ambos deben evolucionar paralelamente, pues de lo contrario estaremos haciendo más difícil la vida del desarrollador que tenga que mantener el software, al facilitarle pistas incorrectas para comprenderlo.

Atención especial a las refactorizaciones automáticas, que suelen introducir cambios en distintos puntos del código de un proyecto, dejando los comentarios obsoletos en ese mismo instante.

12. La regla de oro del código legible

He dejado para el final uno de los principios básicos para muchos desarrolladores: deja que tu código hable por sí mismo. Aunque se sospecha que este movimiento está liderado por programadores a los que no les gusta comentar su código ;-), es totalmente cierto que una codificación limpia puede hacer innecesaria la introducción de textos explicativos adicionales.

Recordemos, por ejemplo, el código introducido en el post "Interfaces fluidos (fluent interfaces)", donde se muestra lo que esta técnica puede aportar a la claridad y autoexplicación en un desarrollo:

Console.WriteLine("Resultado: " +
new Calculator()
.Set(0)
.Add(10)
.Multiply(2)
.Substract(4)
.Get()
);
 
A la vista del ejemplo, ¿es necesario añadir algún comentario para que se entienda qué hace el código? El uso de nombres apropiados (aconsejo leer el clásico Ottinger's Rules), indentación correcta y la adopción de guías de estilo (podéis ver algunas en la wikipedia, o googleando un poco) facilitan enormemente la escritura homogénea e inteligibilidad directa del código.

El no cumplimiento de esta regla hace que a veces los comentarios puedan parecer una forma de pedir perdón al desarrollador que se encargará del mantenimiento del software.

13. Difunde estas prácticas entre tus colegas

Obviamente, aunque ya hemos comentado en el punto 10 que nosostros mismos nos beneficiamos inmediatamente de las bondades de nuestro código comentado, la generalización y racionalización de los comentarios y la creación código inteligible nos favorecerá a todos, y sobre todo en contextos de trabajo en equipo. No dudes, por tanto, en crear cultura de comentarios en tu entorno.

Publicado en: Variable Not Found.
miércoles, 28 de noviembre de 2007
De vez en cuando reviso las palabras por las que se llega a Variable Not Found a través de los buscadores, más que nada porque suelen reflejar dudas o problemas comunes de los desarrolladores y pueden servir como inspiración sobre nuevos temas a tratar en el blog.

Pues bien, llevo tiempo observando que hay muchas personas que preguntan a Google algo similar a "anular postback en botón", es decir, se tienen dudas sobre cómo evitar el disparo del postback al pulsar un botón de un formulario ASP.Net. El tema me ha parecido tan interesante que intentaré dar una respuesta a la duda estudiando algunas de las distintas posibilidades que pueden darse. Eso sí, si echáis en falta algún caso, no dudéis en comentarlo y lo trataremos lo antes posible.


I. ¿Hablamos de botones con lógica exclusivamente en cliente?

La primera cuestión es preguntarse para qué queremos un botón que no realice un postback al ser pulsado. Pensad que si todas las acciones desencadenadas por la pulsación del botón se realizarán en cliente es posible que no necesitemos un control ASP.Net. Imaginemos, por ejemplo, un botón que queremos que haga un cálculo en el cliente y muestre un cuadro de alerta con el resultado... ¿es necesario que sea un control de servidor (runat="server")? Seguro que no.

Para este caso lo más apropiado es incluir en el .aspx un HTML INPUT de los de toda la vida:

<input id="Button1" type="button" value="button"
onclick="hacerAlgo();" />
 
Si eres de los que prefieren arrastrar y soltar, puedes hacer exactamente lo mismo utilizando los controles incluidos en la pestaña HTML de Visual Studio, en este caso el llamado Input(Button). El resultado visual será idéntico al de un Button (un botón de servidor) y estaremos aligerando tanto la página enviada al cliente como la carga del servidor.

II. Anular el postback en un botón de servidor

Otra posibilidad es que vayas a usar un control de servidor (un Button, por ejemplo) y sólo bajo determinadas circunstancias quieras anular el postback desde el cliente. Obviamente, estas circunstancias serían calculadas/obtenidas por script, por lo que podríamos aplicar un patrón como el siguiente:

...
<asp:Button ID="Button1" runat="server"
Text="Pulsa"
OnClientClick="return comprueba();" />
...
<script type="text/javascript">
function comprueba() {
return confirm("Confirme el postback");
}
</script>
 
Como se puede observar, se ha modificado la propiedad OnClientClick del botón para hacer que retorne el resultado de la llamada a la función comprueba(). Si ésta retorna cierto, se realizará el postback, mientras que si retorna falso, no lo hará. En el ejemplo anterior se permite al usuario decidir si desea enviar el formulario al servidor o no.

III. Anular la posibilidad de envíos múltiples

Hay otras ocasiones, sin embargo, en las que estamos utilizando un botón de servidor de forma normal y simplemente queremos evitar duplicidades en el envío del formulario, es decir, bloquear el botón correspondiente una vez el usuario lo haya pulsado la primera vez.

Esto es muy habitual debido a la falta de feedback: el usuario envía el formulario, se impacienta esperando la respuesta o no está seguro de haberlo enviado, y cual poseso, inicia un ataque implacable sobre el botón de envío. ¿A que os suena?

El comportamiento típico, adoptado por la mayoría de sitios web, es deshabilitar el botón y seguir con el postback. Sin embargo, esto que podría ser realmente sencillo mediante scripting, causa algún que otro problema cuando el botón es de tipo Submit, puesto que en determinados navegadores (por ejemplo IE6) no realizará el envío si lo está generando un botón deshabilitado. Una forma de evitar este problema es hacer que el botón no sea de tipo submit e incluir un pequeño script:

<asp:Button ID="Button2" runat="server"
Text="Pulsa"
OnClientClick="this.disabled=true"
UseSubmitBehavior="False"/>

Si el botón debe ser obligatoriamente de tipo submit, se deben utilizar soluciones alternativas. Googleando un poco he encontrado varias, como deshabilitar el botón pasadas unas décimas de segundo mediante un timer, o retocar los estilos del botón para, simplemente, hacerlo desaparecer y mostrar en su lugar un mensaje informativo. La mejor, para mi gusto, consiste en capturar el evento onsubmit del formulario web, así:

...
<form id="form1" runat="server" onsubmit="deshabilita()">
...
<script type="text/javascript">
function deshabilita()
{
var btn = "<%= Button1.ClientID %>";
if (confirm("Confirme postback"))
{
document.getElementById(btn).disabled = true;
return true;
}
return false;
}
</script>
 
Este último ejemplo lo he aprovechado para, además, mostrar cómo es posible realizar la captura del envío justo antes de que se produzca, en el evento OnSubmit, e introducir las confirmaciones de envío y deshabilitado de botones.

Publicado en Variable not found.
martes, 27 de noviembre de 2007
Aunque he visto que es conocido desde hace tiempo, yo me he enterado ahora de que en http://www.connectionstrings.com/ hay una excelente recopilación de cadenas de conexión a fuentes de datos de todo tipo:
  • Motores de bases de datos SQL Server, SQL Server 2005, SQL Server 2005 Compact Edition, Oracle, MySQL, Interbase, IBM DB2, Sybase, Informix, Ingres, Mimer SQL, Lightbase, Postgre SQL, Paradox, Firebird, AS/400 (iSeries), Pervasive, SQLBase, Progress y Caché.

  • Archivos de datos Excel 2007, Excel, texto plano, Access 2007, Access, Visual FoxPro / FoxPro 2.x DBF / FoxPro, SQLite y Filemaker.

  • Otras fuentes, como MS Project, Active Directory, Exchange, Lotus Notes, DSN y UDL.

Seguro que algún despistado (como el menda, todo sea dicho), todavía no lo conocía. Y es que no hay nada como internet para tener a mano toda la información que necesitamos.

Publicado en: Variable not found
lunes, 26 de noviembre de 2007
Hasta hoy no sabía que la arroba, además de permitir la definición de constantes string literales y multilíneas (mi gran descubrimiento del mes pasado), también puede ser utilizado en C# para utilizar nombres reservados del lenguaje como identificadores.

Según la especificación del lenguaje C#, un identificador (de una variable, clase, propiedad...) puede ser una de las palabras clave del lenguaje (for, if, int, string...) si va precedida por una arroba, permitiendo aberraciones como la siguiente:

class @class
{
public static void @static(bool @bool)
{
if (@bool)
System.Console.WriteLine("true");
else
System.Console.WriteLine("false");
}
}
 
En fin, dudo mucho que vaya a utilizar esta posibilidad alguna vez, pero está bien saber que existe.

Publicado en: Variable Not Found.
domingo, 25 de noviembre de 2007
Si existieran los diez mandamientos del programador, seguro que uno de ellos sería "comenta tu código". Y es que está claro que la mantenibilidad de una aplicación o módulo es posible siempre que los profesionales encargados de ella sean capaces de entender perfectamente qué hace el software y cómo lo hace, y es aquí donde un código correctamente documentado puede facilitar enormemente la tarea.

Todos los lenguajes de programación facilitan la inclusión de texto libre, no estructurado, con objeto de que el programador explique a futuros mantenedores del software los principales aspectos del código fuente que está observando, el por qué de determinadas decisiones, o, en definitiva, aclarar lo que considere oportuno. Sin embargo, esta libertad y falta de normalización acarrea una serie de inconvenientes que deben ser tenidos en cuenta, y que comentó Chad Myer hace unas semanas.

En primer lugar, es importante ser conscientes de que estos textos están fuertemente ligados al código que comentan y, obviamente, al no ser comprobados por ningún sistema automático (como un compilador), es fácil que contengan errores:

// Retorna la suma de a y b
public int multiplica(int a, int b)
{
...
 
Tampoco puede asegurarse que los comentarios sean útiles; de hecho, parte de los comentarios que leemos son obviables y totalmente innecesarios, auténticos insultos a la inteligencia, del tipo:

if (a > 5) // Si a es mayor que cinco, ...
counter = 0; // ... ponemos el contador a cero
...
 
La inclusión de este tipo de comentarios no harían sino añadir "ruido" a un código perfectamente inteligible de forma directa.

Y hablando de comentarios prescindibles, es curioso ver su utilización como vía de escape y desfogue de algunos programadores, que no dudan en añadir todo tipo de artillería a sus creaciones, como las mostradas en el Contador de palabrotas del Kernel de Linux, arremetiendo contra los usuarios, colegas o todo aquél que pase por delante en el momento apropiado.

Otro hecho bastante frecuente es que los comentarios sean introducidos al crear un código pero que no sean actualizados de forma paralela a éste, lo que da lugar a textos obsoletos y sin sentido en su contexto. Y no hablo sólo de actualizaciones tiempo después de su creación, también las realizadas minutos u horas después de la programación inicial como consecuencia de optimizaciones y refactorizaciones, incluso realizadas por el mismo desarrollador.

// Si x es true, retorna a+b
// Si no, retorna a-b
public int calcula(int a, int b)
{
...
 
A pesar de todos estos problemas, normalmente generados por falta de rigor durante su creación y actualización, los comentarios son absolutamente necesarios para el futuro mantenimiento de un sistema y deben tratarse como parte fundamental en una pieza de código y prestarle la misma atención que a éste. Recordemos la famosa frase de Martin Fowler:
"Cualquier tonto puede escribir código que entienden las computadoras. Los buenos programadores escriben código que entienden las personas"


Para ello, como dice Bernhard Spuida en su magnífico documento The Fine Art of Commenting, hay que luchar contra actitudes negativas como:
  • el orgullo del programador ("no me hacen falta comentarios para entender lo que hace cualquier código", "yo escribo código que entiende cualquiera"). No te engañes, un código medianamente complejo te llevará tiempo entenderlo en cuanto hayan pasado unos meses incluso a tí mismo, así que imagina a otro desarrollador.
  • la pereza y procrastinación ("ya comentaré el código más adelante"). No te engañes, no lo harás; además, se estima que el tiempo necesario para añadir comentarios útiles a un código se duplica (como mínimo) si no se realiza en el momento de la codificación.
  • la excusa del deadline ("el proyecto va demasiado apurado como para ponerme a comentar el código"). No te engañes, los minutos de tardarás en comentar apenas afectarán a los plazos, y sí añadirán mantenibilidad al sistema.

Y es que, citando a Ryan Campbell en su post Successful Strategies for Commenting Code,
"comentar el código es como limpiar el cuarto de baño; nadie quiere hacerlo, pero el resultado es siempre una experiencia más agradable para uno mismo y sus invitados"


Publicado en: Variable Not Found.
miércoles, 14 de noviembre de 2007
Pues no, esta entrada no trata de los diseños líquidos habituales en el mundo de la maquetación web, aunque podría parecer lo contrario. El término interfaz se utiliza en su acepción relativa a la orientación a objetos, y la fluidez se refiere a la continuidad en el movimiento de instancias entre distintas llamadas a métodos.

En pocas palabras, el uso de interfaces fluidos es un estilo cada vez más frecuente de programación, también llamado a veces "encadenamiento de métodos" (method chaining), que promueve la eliminación de código innecesario y engorroso para la realización de tareas frecuentes, sustituyéndolo por una secuencia natural, intuitiva y fluida de instrucciones que se encadenan de forma casi mágica.

Pero veámoslo con un ejemplo. Y ojo, que aunque lo codificaremos en C#, el concepto es perfectamente válido para otros lenguajes orientados a objetos, salvo por los aspectos sintácticos.

Imaginemos la clase Calculator, con un diseño como el mostrado a la izquierda, que es capaz de mantener un único valor (entero) y que soporta cinco operaciones: Get (para obtener el valor actual de la calculadora), Set (para establecerlo), y Add, Substract y Multiply (para sumarle, restarle y multiplicarle un valor respectivamente).

La forma habitual de operar con esta calculadora para obtener el resultado de un cálculo sería algo parecido a:
  Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine("Resultado : " + calc.Get());
 
Como podemos observar, para realizar unas operaciones muy simples hemos necesitado demasiadas líneas de código, y además bastante repetitivas.

Veamos ahora cómo utilizando la técnica de interfaces fluidos podemos dejar este código en:
  Console.WriteLine("Resultado: " +
new Calculator().Set(0).Add(10).Multiply(2)
.Substract(4).Get()
);
 
El truco consiste en hacer que métodos que normalmente no devolverían ningún valor (métodos void, como los de la clase Calculator anterior) retornen referencias hacia el propio objeto, es decir, acaben con un return this;. De esta forma, la llamada a la función podrá ir seguida de otra llamada sobre el mismo objeto, que a su vez lo devolverá para el siguiente y así sucesivamente. Y de ahí el término "fluido": fijaos en el ejemplo anterior como la instancia de Calculator que es retornada por el constructor se utiliza directamente en la llamada Set(0), que a su vez la devuelve y es utilizada por Add(10)... ¿no es eso fluir, aunque el término suene algo poético?

Otro ejemplo, donde se utiliza la misma técnica, pero esta vez combinando instancias de distintas clases (fijaos que nada impide en la mayoría de lenguajes dividir las líneas de código como se muestra, facilitando así la lectura):

new Surface()
.Clear()
.Fill("white")
.CreateText()
.SetMessage("Hi, all!")
.SetColor("blue")
.Draw()
.CreateCircle(100, 100, 10)
.Fill("green")
.Draw()
.Border("red")
.Draw()
 
Este código muestra tres aspectos importantes. Primero, que es posible crear un pseudo-lenguaje de programación utilizando esta técnica, de hecho los fluent interfaces son muy utilizados en los DSL (Lenguajes Específicos de Dominio). En el ejemplo se puede intuir un lenguaje para el dibujo de figuras sobre una superficie.

Segundo, que la devolución de los métodos no tiene por qué limitarse a una única clase. En el código anterior, los métodos CreateText() y CreateCircle() retornan referencias a nuevas instancias de clases que representan estas figuras, que también hacen uso de interfaces fluidos. Los métodos Draw() de ambas retornan de nuevo una referencia a la instancia de la superficie (Surface) sobre la que están dibujando.

Tercero, se deja entrever la dificultad de desarrollar clases que permitan utilizar esta técnica, pues cada método debe devolver una referencia al objeto oportuno para facilitar el encadenamiento con el método posterior. Una elección incorrecta de la clase de devolución hará que la fluidez se rompa.

Finalmente, decir que los interfaces fluidos no son algo nuevo. Hay quien habla de su uso en SmallTalk antes de la década de los noventa (!), aunque el concepto se extendió a partir de la inclusión de una entrada en la Bliki de Martin Fowler comentándolo hace un par de años.

Hay también quien opina que los interfaces fluidos son algo más que el encadenamiento de métodos, que también existe en un código como name.Trim().ToUpper(), pues aporta una interfaz comprensible e intuitiva.

En cualquier caso, hace algún tiempo que esta técnica me llamó la atención y cada vez lo veo utilizado en más ocasiones, especialmente en contextos de frameworks y para implementar DSLs (como Quaere).
domingo, 11 de noviembre de 2007
Las primeras versiones de la plataforma .Net introdujeron el PostBack como el mecanismo de recarga de una página gracias al cual era posible la persistencia del estado de controles y la interacción con ellos de forma muy sencilla y potente en el lado del servidor, modificando la filosofía de programación de webs que habíamos usado hasta entonces (ASP clásico, CGIs...).

Sin embargo, si querías usar los controles de servidor en todo su esplendor te veías obligado a meter tareas completas dentro de una misma página; así, aquellas que presentaban funcionalidades relativamente complejas se convertían en batiburrillos donde se incluían formularios de entrada de datos, código de validaciones, formularios de salida de información, etc. Por ejemplo, podíamos tener en un mismo Webform paneles de introducción de criterios de búsqueda, paneles de información o errores, paneles con un grid para mostrar los resultados e incluso otro panel para mostrar una vista ampliada de un registro concreto, y jugar con las visibilidades para mostrar unos u otros en función de los pasos dados por el usuario.

Desde ASP.Net 2.0 es posible realizar los llamados "Cross Page PostBacks", o PostBacks hacia páginas distintas de la inicial, permitiéndonos separar funciones y hacer nuestro código un poco más legible, estructurado y reutilizable. El ejemplo anterior podría ser diseñado en tres páginas independientes, separando las distintas vistas, una solución mucho más limpia:
  • en la primera estaría el formulario de introducción de criterios.
  • en la segunda podríamos simplemente tomar los parámetros de la anterior y mostrar un listado con los resultados obtenidos, o bien un mensaje de información si no hay coincidencias.
  • en la tercera podríamos montar una ficha con los datos detallados de un elemento, que habría sido seleccionado por el usuario en la página anterior.
¿Y cómo se hace? Sencillo.

En primer lugar, hay que modificar el comportamiento de los botones de acción (o similar, de hecho también podría ser ImageButtons o LinkButtons) para que realicen el envío a otra página, indicándole su URL en la propiedad PostBackUrl. Eso hará que, tras su pulsación, el control sea enviado inmediatamente al archivo .aspx que se defina en la misma.

Un aspecto muy a tener en cuenta es el término "inmediatamente": el control es transferido a la nueva página sin ejecutar posibles validaciones en servidor, por lo que podrían llegar gazapos a la segunda. Sin embargo, si los validadores son en cliente, se ejecutarán con normalidad (si el usuario no tiene desactivado Javascript, of course!).

Ya en la página destino, hay varias opciones para recuperar el valor de los controles de la página invocadora. La primera de ellas, la más sencilla, consiste en usar la propiedad PreviousPage de la clase Page para obtener una referencia hacia la página de origen y obtener sus controles de la siguiente forma:

if (PreviousPage != null)
{
TextBox txt =
(TextBox)PreviousPage.FindControl("TextBox1");
Label1.Text = txt.Text; // Por ejemplo
}

Sin embargo, no es una solución aconsejable, puesto que un simple cambio de nombre del control en la página origen provocaría un error en tiempo de ejecución, puesto que FindControl no sería capaz de encontrarlo con el nombre "TextBox1".

Hay otra forma mucho más potente, que consiste en especificar en la página destino un tipado fuerte para la de origen usando en su archivo .aspx la siguiente directiva:

<%@ PreviousPageType VirtualPath="~/PaginaOrigen.aspx" %>

De esta forma, cualquier referencia a PreviousPage en la página destino se considerará automáticamente del tipo de la página de origen, pudiendo acceder a sus propiedades de forma directa, sin necesidad de castings ni nada parecido. Sin embargo, todo sea dicho, debido a que el nivel de visibilidad de los controles de un Webform son protected, no podemos acceder directamente a su contenido, debemos usar accesores o propiedades públicas para ello.

Por ejemplo, podríamos definir en la clase (codebehind) de la página de origen una propiedad como la siguiente como accesor al contenido de un control TextBox llamado txtTexto:

public string Texto
{
get {return txtTexto.Text; }
}

Y en la página destino, una vez introducida la directiva descrita anteriormente especificando la URL de la página de origen, podríamos hacer lo siguiente:

if (PreviousPage != null)
{
Label1.Text = PreviousPage.Texto; // Por ejemplo
}

En resumen, esta técnica permite estructurar mucho mejor nuestras aplicaciones web, aunque sea a cambio de realizar un sobreesfuerzo en el desarrollo. En mi opinión puede valer la pena su uso en páginas que generen demasiadas vistas diferentes para ser introducidas en un único Webform sin comprometer la mantenibilidad, o cuando resulte interesante de cara a la reutilización de páginas.

No es conveniente, a mi entender, su utilización indiscriminada en formularios simples, pues precisamente los PostBacks fueron introducidos en ASP.Net para evitar la proliferación innecesaria de páginas sueltas (.aspx) y facilitar el mantenimiento del estado en la inherentemente desconectada tecnología Web.
lunes, 5 de noviembre de 2007
A raíz de un post anterior sobre scripts de arranque, y sobre todo en la relativa a la compartición del evento OnLoad por varios controladores, me ha llegado por varias vías la siguiente duda: ¿cómo se pueden vincular a este evento llamadas a funciones que incluyan parámetros?

Concretamente, gerardo pregunta cómo podría añadir varios controladores con parámetros al evento OnLoad de la página, como él mismo dice,

La funcion de script que usas para añadir varias funciones al evento onload esta bien si son llamadas sin parametros. La duda es cómo podría hacer lo mismo si la función que tiene que correr en el inicio recibiera un parametro de entrada.Siguiendo tu ejemplo, me gustaria usar algo como:

addOnLoad(init1(3));
addOnLoad(init2(4));


Efectivamente, la fórmula que propone este amigo no es válida; de hecho, estaríamos añadiendo al evento OnLoad el retorno de la llamada a init1(3), lo cual sólo funcionaría si esta función devolviera una referencia a una función capaz de ser invocada por el sistema.

Para hacerlo de forma correcta, como casi siempre en este mundo, hay varias formas. Comentaré algunas de ellas.

La obvia

Ni que decir tiene que el método más sencillo es sin duda introducir las llamadas en el atributo onload del elemento body de la página, es decir,

<body onload="alert('hola 1'); alert('hola 2');">
 
Por desgracia no siempre es posible modificar el atributo onload del cuerpo de la página, así que lo mejor será estudiar otras maneras.

La fácil

También podríamos utilizar un modelo bastante parecido al indicado en el post inicial, vinculando al OnLoad una función que, a su vez, llamará a las distintas funciones (con los parámetros oportunos) de forma secuencial:

// Asociamos las funciones al
// evento OnLoad:
addOnLoad(miFuncion1);
addOnLoad(miFuncion2);

function miFuncion1() {
alert('hola 11');
alert('hola 12');
}
function miFuncion2() {
alert('hola 2');
}
 

La "pro"

Aunque cualquiera de los dos métodos comentados hasta el momento son válidos y funcionan perfectamente, vamos a ver otra en la que se utiliza una característica de javascript (y otros lenguajes, of course), las funciones anónimas.

Las funciones anónimas son como las de toda la vida, pero con un detalle que la diferencia de éstas: no tienen nombre. Se definen sus parámetros y su cuerpo de forma directa en un contexto en el que pueden ser utilizados sin necesidad de indicar qué nombre vamos a darle.

En el caso que nos ocupa, y tomando como base el ejemplo anterior, en vez de invocar a la función addOnLoad() pasándole como parámetro el nombre de la función que contiene lo que queremos hacer, le enviaríamos una función anónima indicando exactamente lo que queremos hacer:

// En este código se hace lo mismo que
// el ejemplo anterior:

addOnLoad(
function() {
alert('hola 11');
alert('hola 12');
}
);
addOnLoad( function() { alert('hola 2'); } );
 
De esta forma la llamada a la función addOnLoad se realiza utilizando como parámetro la referencia a la función anónima, y todo puede funcionar correctamente.
domingo, 28 de octubre de 2007
Inicializaciones de librerías, llamadas a servidor mediante Ajax para obtener datos o porciones de la página, efectos gráficos... son sólo algunos de los escenarios donde resulta interesante utilizar scripts de arranque o (startup scripts), que se ejecuten en cliente en cuanto le llegue la página web.

Esto hay al menos dos formas de hacerlo: llamar a la función deseada desde el evento onload() del cuerpo de la página o bien hacerlo en un bloque de scripts fuera de cualquier función, bien sea dentro del propio (X)HTML o bien en un recurso .js externo. Pero hace tiempo que tenía una curiosidad: ¿qué diferencia hay entre una u otra? ¿cuándo deben usarse? ¿tienen contraindicaciones?

Para averiguarlo, he creado la siguiente página:

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Prueba de scripting</title>
</head>
<body onload="alert('OnLoad()');">
<h1>Prueba Scripting</h1>
</body>
<script type="text/javascript">
alert('Script en HTML');
</script>
<script type="text/javascript" src="script.js"></script>
</html>


Como se puede observar:
  • en el elemento <body> he incluido un atributo onload="alert('onload');".
  • he añadido un bloque script con una única instrucción alert('script HTML');
  • he añadido también una referencia a un archivo script externo, en el que sólo hay una instrucción alert('archivo .js');
Además he jugado un poco con el orden de introducción de los bloques de scripts para comprobar su influencia en el orden de ejecución del código que contienen. El resultado ha sido el siguiente:
  • Se ejecuta en primer lugar el código incluido en los bloques <script> .
  • Si hay varios bloques de script, se ejecuta el código incluido en cada uno de ellos, siguiendo del orden en el que están declarados.
  • No importa si son scripts incrustados en la página o si se encuentran en un archivo .js externo, se ejecutan cuando le llega el turno según el orden en el que han sido definidos en la página, es decir, la posición del tag <script> correspondiente.
  • Por último, se ejecuta el código del evento onload del cuerpo de la página. De hecho, este evento se ejecuta una vez se ha cargado y mostrado la página al usuario, es el último en tomar el control.
Con estos datos, ya se puede intuir en qué casos puede ser conveniente usar un modelo u otro.

Debemos usar onload() si queremos que nuestro código se ejecute al final, cuando podemos asegurar que todo lo que tenía que pasar ya ha pasado; eso sí, mucho ojo, que onload() es un recurso limitado (de hecho, sólo hay uno ;-)), y si no se tiene cuidado podemos hacer que una asignación nuestra haga que no se ejecute la función anteriormente definida para el evento. Esto se evita normalmente incluyendo en el atributo onload de la etiqueda body las sucesivas llamadas a las distintas funciones de inicialización requeridas (<body onload="init1(); init2();"...), o bien desde script utilizando el siguiente código, todo un clásico:

function addOnLoad(nuevoOnLoad) {
var prevOnload = window.onload;
if (typeof window.onload != 'function') {
window.onload = nuevoOnLoad;
}
else {
window.onload = function() {
prevOnload();
nuevoOnLoad();
}
}
}

// Las siguientes llamadas enlazan las
// funciones init1() e init2() al evento
// OnLoad:

addOnLoad(init1);
addOnLoad(init2);

 

En cambio, la introducción de código de inicialización directamente en scripts se ejecutará al principio, incluso antes de renderizar el contenido de la página en el navegador, por lo que es bastante apropiada para realizar modificaciones del propio marcado (por ejemplo a través del DOM) o realizar operaciones muy independientes del estado de carga de la página. Como ventaja adicional, destacar su facilidad para convivir con otros scripts similares o asociados al evento OnLoad().
jueves, 25 de octubre de 2007
Comprobar si una variable de tipo string está vacía es una tarea que seguro realizamos muy a menudo en nuestras aplicaciones. Observando código, tanto propio como ajeno, he visto que existen varias formas de hacerlo, y a veces cometemos errores que pueden ser fácilmente evitables prestando un poco de atención.

Por ejemplo, dada una variable str declarada como string, utilizando en C# la expresión str.Equals("") podemos conocer si str contiene una cadena vacía. Sin embargo, si str contiene un valor nulo (lo cual es perfectamente posible al tratarse de un tipo referencia) se provocará una excepción de tipo System.NullReferenceException, puesto que estamos intentando llamar a un método de un objeto inexistente.

Ocurre lo mismo si intentamos averiguar el número de caracteres que contiene: (str.Length==0) es cierto si la cadena está vacía, pero igualmente revienta si se trata de un valor nulo.

Una posible solución es preguntar previamente si la cadena es nula y sólo en caso contrario preguntar, siguiendo alguno de los dos patrones descritos anteriormente, si se trata de un string vacío, algo así:
((str!=null) && (str.Length==0))
.

Como podéis observar, aunque válido, parece demasiado laborioso para los que tenemos prisa. Otra forma más corta e ingeniosa de poner lo mismo sería la siguiente: "".Equals(str). Como podéis ver, el problema del nulo desaparece, puesto que se llama al método Equals de la cadena constante "" que siempre tiene valor (la cadena vacía).

Sin embargo, cualquiera de los casos anteriormente citados pueden no funcionar correctamente si consideramos los nulos como una forma más de expresar una cadena vacía. Por ejemplo, observad el siguiente código:

private string saludar(string nombre)
{
if ("".Equals(nombre))
return "No saludo a nadie";
else
return "Hola, " + nombre;
}
 

La función retorna el literal "No saludo a nadie" cuando le llega un nombre vacío (""). Sin embargo, si le llega un valor nulo no se cumple la primera igualdad, entraría en el bloque else y retornaría un saludo absurdo, "Hola, ". Y es que hay veces donde semánticamente el nulo y la cadena vacía son idénticos.

Para estos casos es conveniente hacer equivalentes ambos valores, de forma que nuestra función se comporte exactamente igual tanto si recibe un nulo como si le llega un string vacío. Veamos algunas formas de conseguirlo.

Una fórmula que he visto alguna vez consiste en sumar a nuestro string una cadena vacía en algún momento del proceso, es decir, usar algo como: ""+str. Esta expresión devolverá siempre un string vacío o con el valor original de la variable str, pero nunca un nulo. Una vez realizada esta operación, podemos hacer la comparación con .Equals("") o .Length==0, aunque se recomienda este último por su mejor rendimiento.

Otra forma, algo mejor que la anterior, es usar dos comparaciones combinadas con OR: ((str == null) || (str.Equals(String.Empty))). Así, si str es nulo la expresión evalúa a cierto y sólo en caso contrario se realizará la comparación con la cadena vacía (String.Empty es lo mismo que el string "").

Pero sin duda la mejor opción sería usar el método estático introducido en la clase string en la versión 2.0 de la plataforma: string.IsNullOrEmpty(). Siguiendo con el ejemplo anterior, podríamos reescribirlo de la siguiente manera, consiguiendo el efecto deseado:

private string saludar(string nombre)
{
if (string.IsNullOrEmpty(nombre))
return "No saludo a nadie";
else
return "Hola, " + nombre;
}
 
Este sería un buen momento para dar por finalizado el post si no fuera por que existe un inconveniente a este método, algo que puede parecer increíble: en el framework de Microsoft IsNullOrEmpty() puede hacer que tu aplicación reviente en tiempo de ejecución en las versiones de producción (release) de los ensamblados debido a una mala optimización del JIT cuando utilizamos este método dentro de un bucle. No daré más detalles pues hay multitud de páginas hablando del problema (como VTortola, o El Bruno), pero aquí se aconsejan alternativas a utilizar. En fin, que este método, según se lee, debe ser usado con precaución, o incluso evitado si lo pensamos incluir en un bucle.

Llegados a este punto, por tanto, lo aconsejable es quedarnos con fórmulas artesanales como ((str == null)||(str.Length==0), hasta que IsNullOrEmpty esté en condiciones de ser utilizado, podemos suponer que en la próxima versión de la plataforma.

No he comprobado si existe el mismo problema sobre la plataforma Mono, pero dado que son implementaciones distintas lo más probable es que no ocurra, por lo que IsNullOrEmpty podría ser utilizado sin causar dolores de cabeza. A ver si un día tengo un ratillo y lo compruebo.

Hay que ver lo que da de sí una cosa tan simple, ¿eh?
lunes, 22 de octubre de 2007
Como en otros lenguajes y plataformas, .Net permite la creación de enumeraciones de campos de bits, así como su tratamiento de forma muy sencilla e intuitiva para el desarrollador.

A diferencia de las enumeraciones normales, cuyos elementos son habitualmente excluyentes, las de campos de bits permiten la combinación de ellos, permitiendo su utilización en escenarios algo más complejos que los primeros. Fijaos en los siguientes ejemplos.

Si pretendemos almacenar el estado de las luces de un semáforo, donde sólo uno de los elementos está activo, posiblemente optaríamos por crear una enumeración como la siguiente, de lo más tradicional:

public enum EstadoSemaforo
{
Rojo, Amarillo, Verde
}
 
En cambio, si deseásemos almacenar el estado de las lucecillas del teclado de nuestro PC (BloqMays, BloqNum y BloqDespl), tenemos un total de 8 combinaciones de estados distintos (desde el "todas apagadas" hasta "todas encendidas"), lo cual ya se convierte en incómodo para utilizar una enumeración tradicional de selección única. Y es aquí donde saltan a la palestra los campos de bits, permitiéndonos hacer cosas como esta:

[Flags]
public enum LedStatus
{
BloqMays=1,
BloqNum=2,
BloqDespl=4,
All=BloqMays | BlogNum | BloqDespl,
None = 0
}

Vamos a explicarlo paso a paso. En primer lugar, nos encontramos con el atributo [Flag], que indica a .NET que la enumeración es de este tipo y que debe permitir la combinación de elementos como veremos un poco más adelante.

A continuación se define la enumeración como siempre, aunque estamos forzando a que cada elemento de interés se asocie a un valor potencia de 2, que corresponderá con el valor decimal de su representación binaria. De esta forma, cuando combinemos elementos, su suma lógica nos dará valores únicos que permitirán determinar cuáles de ellos se están uniendo. El siguiente cuadro quizás ayude a entender esto mejor:

Como podemos ver, a cada elemento de la enumeración se asigna un bit en el interior de un byte (o conjunto de bytes); así, BloqMays está asignado al bit 0 (con un valor en decimal de 2^0=1), BloqNum al bit 1 (con valor 2^1=2), etc.

De esta forma, cuando estén activas las luces del bloqueo de mayúsculas (BloqMays) y el del desplazamiento (BloqDespl), estarán a uno los bits 0 y 2, mientras que el bit 1 estará a cero, resultando el número "101" cuyo valor será cinco.

Siguiendo con el ejemplo, tenemos un elemento ("All") que se define como combinación de los anteriores. Esto puede ser util para facilitar las comparaciones con combinaciones muy frecuentes o cuya agrupación tenga un sentido especial, como podría ser un elemento del tipo SuperUsuario en una enumeración de tipos de usuario de un gestor de contenidos:

public enum TipoDeUsuario
{
Anonimo = 0,
Registrado = 1,
Gestor = 2,
SuperUsuario = Registrado | Gestor;
}
 
Volvemos ahora a algo que antes dejamos un poco a medias... ¿para qué el atributo [Flags] que adorna la enumeración? ¿No podemos hacer prácticamente lo mismo sin él, simplemente usando valores potencia de dos en las enumeraciones? Y la respuesta es sí, exactamente lo mismo. La única diferencia que he encontrado es que el método ToString() es capaz de mostrar la combinación de valores, es decir:

LedStatus leds = LedStatus.BloqMays | LedStatus.BloqNum
Console.WriteLine(leds);
 
Si hemos utilizado el atributo [Flags] por consola podremos ver el texto "BloqMays, BloqNum". Si no lo usamos, sólo aparecerá "3", el valor numérico de la combinación de ambos elementos. Supongo que alguna diferencia más habrá, pero como digo, no la he encontrado.

Por último, comentar que existen varias ventajas a la hora de decidirse a utilizar este tipo de enumeraciones. Sin duda, se trata de una forma realmente compacta de almacenar información de estado o indicadores combinables. Eso, aunque hoy en día cada vez tiene menos importancia, era hace tiempo motivo más que suficiente para propiciar su uso en programación ligada al bajo nivel, como en C o ensamblador, donde además existe mucha facilidad para el tratamiento de bits.

También es ventajosa su versatilidad a la hora de tratar los valores. Con conocimientos básicos de aritmética binaria podemos hacer cosas como alternar entre activo e inactivo (operador ~ o Xor), activar varios elementos de golpe (usando máscaras OR) o desactivarlos (máscaras AND), realizar desplazamientos laterales binarios a derecha o izquierda (L ó RShifts), etc.

Sin embargo, el uso de estas enumeraciones presenta varios inconvenientes también relacionados con su naturaleza binaria. Citar, por ejemplo, que la asignación hay que realizarla mediante operadores de combinación lógicos de bit para no alterar el estado de otros indicadores, es decir:


// la siguiente instrucción activa el
// BloqMays, pero desactiva todos los demás

estado = LedStatus.BloqMays;

// las siguientes instrucciones
// activan el bit 0 (BlockMays)
// dejando intactos los demás leds:

estado = estado | LedStatus.BloqMays;
estado |= LedStatus.BloqMays;
 
De la misma forma, no podemos realizar comparaciones utilizando los operadores de igualdad, puesto que sólo evaluarán a cierto sin los dos operandos son estrictamente idénticos:

if(estado==LedStatus.BloqMays)
// sólo se cumple si está
// activo únicamente BloqMays
 
Las comparaciones deben realizarse utilizando de nuevo operadores de aritmética binaria, concretamente el AND lógico, de la siguiente forma:

if ( (estado & LedStatus.BloqMays) != 0)
// se cumple cuando el BloqMays
// está activo, independientemente del resto
 

Publicado en: www.variablenotfound.com.
jueves, 18 de octubre de 2007
Si hace unos días Jeffrey Palermo recogía en su blog la presentación del futuro ASP.Net MVC Framework, es ahora el propio Scott Guthrie, uno de los padres de la criatura, el que hace una pequeña introducción en su bitácora sobre esta tecnología que se avecina.

Aunque en el post de hace unos días donde me hacía eco de la presentación ya recogí alguna de las características principales, no está de más visitar el blog de Scott para conocer, de primera mano, por dónde van los tiros. Además comenta que las próximas semanas seguirá publicando artículos explicando cómo funciona el framework, habrá que estar atentos a su página.

Por último, decir que hay una traducción al español del post original en Thinking in .net, de la mano de Juan María Laó.
miércoles, 17 de octubre de 2007
Hace unos días publiqué un post donde comentaba lo que sin duda era mi descubrimiento del mes: la posibilidad de codificar constantes de cadena multilíneas en C# al más puro estilo Heredoc de PHP y otros lenguajes.

Al día siguiente en la oficina pude observar que, como sospechaba, no era una característica muy conocida (aunque todo el mundo sabía que la arroba @ se utilizaba para introducir fácilmente caracteres extraños en los strings) y que su utilidad era enorme a la hora de asignar sentencias SQL largas, porciones de scripts en código, HTML, etc. La legibilidad que aporta al código es increíble.

Sin embargo, hay un detalle importante que olvidé comentar en el post: para concatenar cadenas definidas de esta manera hay que utilizar la arroba en cada una de las subcadenas constantes.

Para que quede más claro, ahí va un ejemplo en C# donde se pretende incluir el valor de la variable "pattern" en una sentencia SQL:

string sql =
@"SELECT product_name,
product_details,
total_rows
FROM (
SELECT product_name,
product_details,
total_rows,
rownum row_counter
FROM (
SELECT product_name,
product_details,
count(*) OVER () total_rows
FROM products
WHERE product_name
like '%" + pattern + @"%'
ORDER BY product_name
)
)
WHERE row_counter between v_start_row and v_end_row;";
 
Y sí, sé que el ejemplo no es muy correcto desde el punto de vista de la construcción de la sentencia SQL (ojo a la inyección SQL), pero creo que ilustra perfectamente la forma de incluir un contenido variable en el interior de una cadena de este tipo.
miércoles, 10 de octubre de 2007
Siempre he envidiado a los desarrolladores PHP, Ruby o a las figuras del shell-scripting por poder utilizar heredoc para representar cadenas de caracteres de longitud considerable.

Por si no te habías encontrado antes con este término, heredoc es una forma de escribir cadenas literales de forma directa, sin preocuparse de caracteres de control, secuencias de escape o saltos de línea de forma manual. Simplemente se indica en el comienzo la marca que permitirá identificar su final y el resultado será el conjunto de caracteres contenidos entre el inicio y la marca, todo incluido.

De hecho, Heredoc es una abreviatura de "Here document", que viene a ser algo así como "documento a continuación".

Un ejemplo para enviar al navegador del cliente una porción de código script correctamente formateado sería el que se muestra a continuación. Se puede distinguir la marca de comienzo (<<<) seguida de la etiqueta que se usará para determinar su finalización (EOT); a continuación va el contenido del literal y, por último, la marca asignada al comienzo (EOT):

<?php
echo <<<EOT
function AddCss()
{
var l=document.createElement('link');
l.setAttribute('type','text/css');
l.setAttribute('rel','stylesheet');
l.setAttribute('href','styles.css');
document.getElementsByTagName('head')[0].appendChild(l);
};
EOT;
?>


Sin embargo, y aquí va la buena noticia, resulta que en C# podemos realizar algo bastante parecido precediendo la cadena por el carácter "@" (arroba), de la siguiente forma:

string script =
@"function AddCss()
{
var l=document.createElement('link');
l.setAttribute('type','text/css');
l.setAttribute('rel','stylesheet');
l.setAttribute('href','styles.css');
l.setAttribute('media','screen');
document.getElementsByTagName('head')[0].appendChild(l);
}";
Response.Write(script);


Su principal ventaja es la capacidad de representar texto estructurado como Javascript, CSS, (X)HTML, XML o SQL de forma muy directa, ya véis el ejemplo anterior. Nada de "trocear" la cadena en líneas e ir concatenando, ni de introducir caracteres de escape para saltos de línea o tabulaciones. Todo un gustazo.

Por citar algún inconveniente, la cadena siempre debe acabar en dobles comillas; por tanto, si queremos usar este carácter debemos introducirlo doblemente (por ejemplo ""hola"" en vez de "hola"). Tampoco se realiza sustitución de variables en su interior (como ocurre, por ejemplo, en PHP), por lo que hay que usar los operadores de concatenación.

Aunque un poco menos, sigo envidiando a los Heredockers.
jueves, 27 de septiembre de 2007
Los que vamos evolucionando a la vez que lo hace la plataforma .Net (bueno, unos cuantos pasos por detrás ;-)), a veces se nos hace difícil superar nuestras costumbres y adaptarnos a nuevos métodos o utilidades que, sin duda, vienen para facilitarnos la vida y ofrecen mejores soluciones a problemas comunes que las que utilizamos de forma habitual.

Un ejemplo lo tenemos con los métodos estáticos xxx.Parse(), útiles para convertir de un string a un tipo valor, como puede ser int32 o boolean. Desde el principio de los tiempos usamos, para obtener un entero desde su representación como cadena, construcciones de tipo:
string num = "123";
int i = 0;
try
{
i = int.Parse(num);
}
catch (Exception ex)
{
// TODO: hacer algo...
}

Sin embargo, la llegada de .NET Framework 2.0 supuso la inclusión del método estático TryParse() en la práctica totalidad de tipos primitivos numéricos (en la versión 1.1 sólo existe en el tipo double), cuya forma más simple de utilización (y también más usual) dejaría el bloque try/catch anterior en uno más compacto y legible:

if (!int.TryParse(num, out i))
// TODO: hacer algo...
 
El método TryParse devuelve true si se ha podido realizar la transformación con éxito, dejando en la variable de salida que le indiquemos el valor parseado, en este caso un int.

Sin embargo, además de para evitarnos dolores en las articulaciones de los dedos, existen otros motivos para utilizar xxx.TryParse() en vez de xxx.Parse(): el rendimiento, sobre todo si se prevé que el número de fallos de conversión será relativamente alto en un proceso. De hecho es el método recomendado en la actualidad para realizar estas conversiones.

Según la prueba de rendimiento de TryParse publicada Microsoft, ejecutada sobre su propia plataforma, la utilización de este método aventaja de forma considerable al modelo try-parse-catch. Por ejemplo, realizando varias simulaciones de parsing de enteros sobre un conjunto de 100 cadenas de las cuales 5 son erróneas, el tiempo de proceso llegar a ser mil veces superior usando este último patrón; si asumimos 20 incorrectas (80% de cadenas correctas), el modelo tradicional puede tardar 4.000 veces más que usando TryParse.

Aunque lo normal no es efectuar un gran número de transformaciones de este tipo y esta diferencia de rendimiento podría entenderse despreciable, podría ser un factor a tener en cuenta cuando estas operaciones se realicen en contextos de gran carga de usuarios o concurrencia, como en sitios web de alto tráfico. Ojo pues a esas aplicaciones (o componentes) que hemos tomado de .NET 1.x y estamos reutilizando en las versiones superiores.

Por cierto, todo lo dicho, aunque los ejemplos están codificados en C#, es válido para Visual Basic .NET y cualquier otro lenguaje que compile sobre la plaforma .NET de Microsoft. A ver si un día tengo un rato y compruebo si el comportamiento en Mono es el mismo.
martes, 18 de septiembre de 2007
A raíz de los posts sobre los tipos anulables en .NET (introducción a los nullables y uso tipos anulables con enumeraciones), mi amigo Javi me hizo una pregunta que sólo un friki como él sería capaz de formular ;-): ¿cómo afecta el uso de tipos anulables a la serialización de clases?

Aunque sospechaba la respuesta, he hecho un par de pruebas a través de las cuales, ahora sí, puedo asegurar que no afectan en nada, es decir, el sistema controla perfectamente la forma en que se serializan y deserializan este tipo de valores.

El comportamiento es el siguiente: cuando el tipo anulable contiene un valor no nulo, la serialización la delega en el tipo subyacente. Si es un int?, se realizará la serialización por defecto para el int. Usando un serializador XML, un ejemplo de salida sería:


<?xml version="1.0" encoding="utf-16"?>
<int>10</int>

 

En cambio, cuando se trata de serializar el valor null, el sistema genera una etiqueta que indica este hecho, de la misma forma que lo haría si estuviéramos haciéndolo para un tipo referencia. Un ejemplo de salida XML sería:


<?xml version="1.0" encoding="utf-16"?>
<int xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:nil="true" />

 

En fin, que hoy hemos aprendido una cosa más. Y que no falte. :-)
viernes, 14 de septiembre de 2007
De todos es sabido que las enumeraciones son un tipo valor, y se representan internamente como Byte, Int32 u otros tipos primitivos de la plataforma, lo que hace imposible la asignación de valores como null, concebido para los tipos referencia.

Así, en el siguiente ejemplo en C#:

Estado estado1 = Estado.Correcto;
Estado estado2 = null;

La segunda línea provoca el error en compilación "No se puede convertir null en 'ConsoleApplication1.Program.Estado' porque es un tipo de valor". Lógico, es igual que que si intentáramos asignar el maldito valor nulo a un integer.

Esto puede causar ligeras molestias para la gestión de valores "indeterminados", puesto que estaríamos obligados, por ejemplo, a utilizar otro miembro de la enumeración para representar este valor, como en el siguiente código en Visual Basic .Net:

Public Enum Estado
Indeterminado = 0
Correcto = 1
Incorrecto = 2
End Enum

Sin embargo, hay ocasiones en la que resulta más interesante poder disponer del valor nulo como una opción para las variables del tipo de nuestra enumeración. Por ejemplo, si estamos desarrollando componentes que leen de una base de datos donde la indeterminación se representa por un nulo, sería más natural poder trasladar esta particularidad al modelo de objetos.

Y para esto inventaros los tipos anulables, de los que ya publiqué un post hace tiempo (Nullable Types, o cómo convivir con los nulos). Dado que las enumeraciones son tipos valor, nada impide crear tipos anulables de enumeraciones:

' VB.NET
Dim estado1 As Nullable(Of Estado)
Dim estado2 As Nullable(Of Estado)
estado1 = Estado.Correcto
estado2 = Nothing

// C#
Estado? estado1 = Estado.Correcto;
Estado? estado2 = null;

 
miércoles, 12 de septiembre de 2007
Javascript, ese lenguaje tan de moda, nos permite manipular en cliente algunas propiedades de los utilísimos validadores de ASP.Net.

La función ValidatorEnable, proporcionada por la plataforma .Net en cliente y utilizable mediante scripting, nos permite habilitar o deshabilitar validadores de nuestros Webforms sin necesidad de hacer un postback. Un ejemplo de uso sería el siguiente:

function onOff(validatorId, activar)
{
var validator =
document.getElementById(validatorId);
ValidatorEnable(validator, activar);
}

El segundo parámetro sería el booleano (true o false) que indica si se desea activar o desactivar el validador. El primer parámetro es el ID en cliente del mismo, y podemos obtenerlo usando la propiedad ClientID del control; por ejemplo, imaginando que tenemos un validador de tipo RequiredFieldValidator llamado Rfv en nuestro Webform, una llamada a la función anterior sería algo así como:

function algo( )
{
onOff("<%= Rfv.ClientID %>", true);
}

 
lunes, 10 de septiembre de 2007
Hoy seguiremos jugado un poco más con las plantillas de Blogger, y estudiaremos cómo modificar la forma en que este gestor de blogs, sobre el que está montado Variable not found, muestra el mensaje al pie de los posts indicando el número de comentarios de cada entrada, que es bastante soso (al menos en las plantillas por defecto).

Por defecto Blogger nos deja configurar exclusivamente qué texto aparecerá detrás del número de comentarios, es decir, a lo más podríamos hacer que apareciera "3 comentarios, ¡comenta ya!" o mensajes así, pero nunca con lógica dependiente del número, lo cual resulta demasiado lineal y cutre para un blog de renombre como el nuestro ;-)

A lo largo de este post vamos a modificar la plantilla para hacer que se muestre un mensaje cuando no hay ningún comentario, otro distinto cuando hay sólo uno, y otro para el resto de los casos. Esto podríais después complicarlo todo lo que queráis, pero de momento con lo que vamos a ver será suficiente para que podáis empezar a jugar.

En primer lugar he de decir que, al igual que ocurría el otro día cuando tratamos el tema de modificar la forma de aparecer la lista de categorías en Blogger, lo más prudente es hacer una copia de seguridad de la plantilla, pues cualquier paso en falso puede tumbar nuestra bitácora. Por si no viste el post anterior, la forma de hacerlo era accediendo al panel de administración de la herramienta y pulsando el enlace "Descargar plantilla completa" que encontrarás en la pestaña "Plantilla", opción "Edición de HTML".

Una vez realizado el backup, podemos continuar sin peligro.

A continuación, marcamos la casilla "Expandir plantillas de artilugios", que nos permitirá acceder al código fuente completo de los Widgets, que son los elementos, bloques o controles que componen la interfaz de Blogger.

Una vez marcada esta casilla, podemos desplazarnos sobre el texto y buscar una porción de código similar a este, donde se ve claramente el comportamiento por defecto del gestor a la hora de mostrar el número de comentarios de una entrada:
  <b:if cond='data:post.numComments == 1'>
1 <data:commentLabel/>:
<b:else/>
<data:post.numComments/> <data:commentLabelPlural/>:
</b:if>


A simple vista se entiende el comportamiento por defecto del sistema, ¿no? Fijaos que se distingue cuando hay un único comentario para poner la etiqueta en singular, mientras que en resto de casos es en plural.

Si escudriñáis en el código de la plantilla, detectaréis que hay dos puntos donde aparece una porción similar. Uno de ellos es para indicar la presentación en la página principal de vuestro Blog, donde aparece la lista de los últimos posts; el otro es para la página individual de cada post. A efectos prácticos, lo que quiere decir es que podemos establecer reglas diferentes para cada caso, aunque por simplificar un poco el ejemplo no lo haremos aquí.

Pues bien, vamos a modificarlo ligeramente para animar al lector a participar en el blog con sus comentarios. Por cierto, no me gusta mucho moverme en el interior del textarea de Blogger. Prefiero llevarme el código a un editor de texto de mi equipo, y desde ahí manipularlo todo lo que quiera. Una vez he acabado de hacer los cambios, copio y pego sobre el cuadro de edición y listo; o si lo he salvado a mi disco, también podría subirse usando la opción de cargar o subir la plantilla disponible en la parte superior de la pantalla.

En cualquier caso, el nuevo código a incluir sería:
<b:if cond='data:post.numComments == 1'>
Ya hay 1 comentario, ¡participa!
<b:else />
<b:if cond='data:post.numComments == 0'>
No hay comentarios. ¡Sé el primero!
<b:else />
<data:post.numComments/> <data:commentLabelPlural/>:
</b:if>
</b:if>

Como se puede comprobar, si sólo hay un comentario lo indicamos y hacemos un llamamiento directo a la participación del usuario. Si no hay comentario alguno, animamos a ser el primero en comentar, y en el resto de los casos, hacemos como siempre.

Vamos, que ya no hay excusa para que en tu blog no haya participación. ;-D