![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZFM6MXxwB5I3dgiQTwAel9Oi2fLNXihk36qzahyphenhyphenub0FRHNeOWIjjfzK0Tgg4K1UvCWIlGOFUdzTjY9X4QohrnJubb_ZNbe8NQoTernMYmRZdvH1qYzhEKsxTqiO6sMZe351xWIQ/s200/programacion.jpg)
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 castingvar 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...](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinLiqSiFFlMxxgjY1PKRvm9UYQRbvnhJW9tjOGBNjN6sW-sBAUrYhgs8F9nIwgRJvx0QcvgS3tHHS_0sVK683elZthmAkR1YuDJAyZRGtTEXoMCPl8Rr2OXYVtrxnfjRnjXQQsng/s320/ImplicitTypes.jpg)
Implicitly-typed local variables must be initializedPor 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.
(Las variables locales implícitamente tipadas deben ser inicializadas)
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxQUEhyphenhyphen9ckeJ-kgtXNVQNp-3xsRZ3PoIJQMGzAyuYDyx1FVPJhM-gTPwKl17Us8zdgSo9DnbvULo6rC4r_oUrrD_mBZ8fTZB_HaAxoNgzO-6kXuPNcLqeXKygtDPziWJtJ4k2FVQ/s400/generarpara2.0.jpg)
Publicado en: Variable Not Found.
Publicado por José M. Aguilar a las 9:47 p. m.
Etiquetas: .net, asp.net, buenas prácticas, c#, nivel medio, programación, vs2008
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
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.
Publicado por José M. Aguilar a las 8:36 p. m.
Etiquetas: buenas prácticas, comentarios, desarrollo, programación
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.
- 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
Publicado por José M. Aguilar a las 7:30 p. m.
Etiquetas: bases de datos, cadenas de conexión, connection strings, desarrollo, programación, utilidades
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.
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.
Publicado por José M. Aguilar a las 8:35 p. m.
Etiquetas: buenas prácticas, comentarios, desarrollo, programación
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirMF84j1wJCPWEj2VpSKZ8iwE7UEGqwsW224KCrXZSxEkAfox7b959tOSuQ7F1JoAml_25Qg2Om_nha1C9-j53vah63oaXm9BE6IUBtVBX02_ODNJes72qG1KhPb3STNhf-63zWg/s320/calculator-class.gif)
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).
Publicado por José M. Aguilar a las 8:48 p. m.
Etiquetas: desarrollo, dsl, fluent interfaces, interfaces fluidos, patrones, programación
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.
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.
Publicado por José M. Aguilar a las 11:38 p. m.
Etiquetas: asp.net, c#, desarrollo, mono, programación, web
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.
Publicado por José M. Aguilar a las 9:10 p. m.
Etiquetas: desarrollo, javascript, programación, scripting, trucos, web
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLCa7Hfjb0jAqAtoTN0ZSd7FyhZ3NNX5MM1v-a75ufwgOUoSgPfMet_6kSn3lmQLAV1N-filDoHOba_n8wtUt-i3nopKI-vz9nXDB-67AjNrQHLyXbp-LMXxx75o0ONvX0jdbQ9g/s320/browsers.png)
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');
- 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.
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().
Publicado por José M. Aguilar a las 10:14 p. m.
Etiquetas: desarrollo, html, javascript, programación, scripting, web
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZFM6MXxwB5I3dgiQTwAel9Oi2fLNXihk36qzahyphenhyphenub0FRHNeOWIjjfzK0Tgg4K1UvCWIlGOFUdzTjY9X4QohrnJubb_ZNbe8NQoTernMYmRZdvH1qYzhEKsxTqiO6sMZe351xWIQ/s200/programacion.jpg)
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?
Publicado por José M. Aguilar a las 9:00 p. m.
Etiquetas: .net, c#, desarrollo, nivel básico, programación, trucos
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:
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyiIem_hJI2fFFvz2yV6mp_w-eK9yVB9z8tZzLVahMOTBEb3nZqnm3Ehgv8HPqglSDrQaZIc7hjRlqXKZtVDsxPnF0KPiy6RNWCZYbdQo-v20zb01AYqVDdjQS3LdkDds1wRqm-Q/s400/programacion.jpg)
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.
Publicado por José M. Aguilar a las 8:10 p. m.
Etiquetas: .net, asp.net, c#, desarrollo, programación
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghXKpOh2k7uVBUvv2Ky5UHE4YlkLR8FCdheoxxeVTXX3s3Tb0URqQLsFxVJNQBbWb1-vxcONzP_Nb2cTDcPDICPJ4DmZAK_olJIi-VpGxq3YbLfCYb2YvualjWCh_MIgURjtMTDQ/s320/step1.jpg)
Publicado por José M. Aguilar a las 8:05 p. m.
Etiquetas: .net, asp.net, aspnetmvc, desarrollo, frameworks, microsoft, programación, tecnología
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHfjraE57QWNW6kd6z1SGg12RVum9cWQerJ0DF4Nf420O2fwDdwTWc8nOG5lp4-SW8QaIsOvvshrH5ry8y0z74lU7qXYGH8BQ9OsxCxC7oXunUobUB0S9hbI_TRo1gwWcCtGoSTA/s200/phpcode.jpg)
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.
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRotJ7GiSW3Whj2ZYPRjVpuc5LqqZF2JtpUCjwTBpHTyl0Q6ZsxiMu2CDFCfODqdcAqYJUITwZpBWyKwf_lgDcgoMZNfH-OLLLnpPXs6zKoQINk4Br8SfPRMj1QHvW6inrRCdRnA/s320/TryParseTest.gif)
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.
Publicado por José M. Aguilar a las 7:40 p. m.
Etiquetas: .net, asp.net, c#, desarrollo, mono, programación, rendimiento, vb.net
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6lyzIa76JpQOtvzpHXYI6hncf9WGJxLKCuaywPhCojZscHAQrdhn6Z-8kRKnm5sQk9Coo_nVygm1UOcgY5NSeL086fH9yRBZDO5-8-e0EkOSNkdYZnDzmt5tqrAj9oWfiGXx6Rw/s320/source_code.jpg)
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. :-)
Publicado por José M. Aguilar a las 9:10 p. m.
Etiquetas: .net, c#, desarrollo, estándares, programación, vb.net
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0O85-TgMKNclxeKXlDI5LGaubFSdBLc8bCgrKdJEGaTya7z1ZjM0NR_5Icd-mFjAMNEZO-jcZagl8Nb7vzuEe-88I84dcUppiLdTDIbX0SUBok1FjsC8qinQQISAPofEw8BMyCA/s320/csharp.jpg)
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;
Publicado por José M. Aguilar a las 8:43 a. m.
Etiquetas: .net, c#, desarrollo, programación, trucos, vb.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);
}
Publicado por José M. Aguilar a las 9:02 p. m.
Etiquetas: .net, asp.net, c#, desarrollo, mono, programación, trucos, vb.net, web
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.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdVp89vVEGirV9qQquVieQg4axa0sl7SXbREsUjAgpa5h0ohObX-aa7kYVUB2NBRkjybh_kqO1w2NUXP-teLwaTEihaa6n0hRXND6tWvOS87qp5aeT9y4eajIPVlzL6hgKlI2Y4Q/s400/SalvarPlantilla.png)
Una vez realizado el backup, podemos continuar sin peligro.
![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTjD0rctDpg32INr8xHgoYFselZ_ODEGR1pcsLYXzeBDjhSqJREAMSiP4XlVSX33MBwsZR1eSFvIV7OTmtOkExlassdF-rjamsknL71Jss99iFb8orhgqqguBBur8dlzzzYT9CLg/s400/expandir.png)
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
Publicado por José M. Aguilar a las 8:35 p. m.
Etiquetas: blogger, blogging, comentarios, programación, trucos