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!
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.
martes, 16 de octubre de 2007
Desde este mismo momento, y gracias a la gentil invitación del amigo Rodrigo Corral, estoy publicando en Geeks.ms, una selecta comunidad de desarrolladores orientada a la programación con y para tecnologías de Microsoft. La compañía no puede ser mejor: un grupo de auténticos maestros, MVPs, MCP ó MCT como El Guille, Jorge Serrano, el mismo Rodrigo, Sergio Tarrillo, El Bruno, y un larguísimo etcétera (que no se ofenda nadie, en algún momento hay que acabar la lista ;-)).

Por mi parte, seguiré con lo mismo que vengo haciendo desde hace ya año y medio en Variable Not Found: escribir sobre lo que más me gusta, el mundo del desarrollo de software. La diferencia es que ahora llegará a más lectores. :-)

De momento, hasta que descubra otra forma más apropiada, realizaré crossposting artesano (vamos, copiar y pegar) de las entradas de VariableNotFound.com hacia mi dirección en Geeks, http://geeks.ms/blogs/jmaguilar.

Nos vemos también por allí.
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.