En código que veo, incluso escrito por mí un tiempo atrás, es muy habitual encontrar comparaciones de cadenas de caracteres en las que, para asegurar que el casing no sea tenido en cuenta, se fuerza una conversión de alguno de los operandos, o incluso ambos, a mayúsculas o minúsculas.
En escenarios muy simples esto funcionará bien y no tendrá contraindicaciones especialmente graves para nuestro sistema. Veamos unos ejemplos:
// Ejemplo 1: conversión de un único operando
int Calculate(string op, int a, int b)
{
// Pasamos a minúsculas el operador para
// asegurar que encaja con la constante
if(op.ToLower()=="add")
{
return a+b;
}
else if(op.ToLower()=="sub")
{
return a-b;
}
...
}
// Ejemplo 2: conversión de ambos operandos
bool AreBrothers(User user1, User user2)
{
// Pasamos a mayúsculas ambos apellidos por
// si alguno se ha escrito usando otro casing
var areBrothers = user1.Surname.ToUpper() == user2.Surname.ToUpper();
return areBrothers;
}
Sin embargo, aunque pueda parecer despreciable, estas operaciones de transformación a mayúsculas o minúsculas tienen un coste importante, que se pone especialmente de manifiesto cuando estamos hablando de aplicaciones con mucha carga de usuarios, alojada en infraestructura muy ajustada o cuando se requiere un rendimiento extremo.
Publicado por José M. Aguilar a las 8:05 a. m.
Etiquetas: .net, nivel básico, optimización
Pero aunque indudablemente los tipos booleanos o flags son una fórmula muy compacta para almacenar información, el mundo suele ser mucho más complejo y estas simplificaciones son a menudo origen de problemas y trampas para nuestro yo del futuro.
En este post vamos a ver algunos escenarios en los que este tipo de dato puede llegar a complicarnos la vida.
Pero comencemos desde el principio...
Hay varios algoritmos para conseguirlo, pero el llamado Fisher-Yates shuffle es muy eficiente (O(N)), no necesita almacenamiento extra, es fácil de implementar y ofrece unos resultados más que razonables. Este algoritmo permite generar una permutación aleatoria de un conjunto finito de elementos o, en otras palabras, desordenar los elementos de un array.
Cada vez que tengo que forzar la validación de los datos de un formulario Webforms mediante javascript me veo obligado a preguntarle a Google, ese que todo lo sabe, cómo era el nombre de la función. Cosas de la edad, supongo ;-)
Así que, a modo de auto-recordatorio y con la intención de que pueda ser útil a alguien más, ahí va: la función se llama Page_ClientValidate()
. Retorna “true” si, una vez evaluados todos los validadores de la página, el valor de los campos es correcto (al menos en cliente; otra cosa son las comprobaciones en servidor, p.e., las definidas en un CustomValidator
).
Y como ejemplo de uso, puede valer el siguiente. Se trata de un botón de envío en un formulario donde se compone un correo electrónico:
1: ...
2: <asp:Button ID="btnEnviar" runat="server" Text="Enviar mail"
3: OnClick="btnEnviar_Click"
4: OnClientClick="return confirmar();"
5: />
6: ...
7:
8: <script type="text/javascript">
9: function confirmar() {
10: if (!Page_ClientValidate()) // Fuerza la validación en cliente
11: return false;
12:
13: return confirm('¿Seguro que desea realizar el envío?');
14: }
15: </script>
Como se puede observar, en el atributo OnClientClick
del botón incluye un script en el que se retorna el valor devuelto por la función confirmar
. Si el retorno es false, se cancela el Postback, evitando así que se invoque al evento btnEnviar_Click
que es el que realiza el envío del mail propiamente dicho.
En el cuerpo de la función confirmar()
, en primer lugar, validamos la página; si ésta no supera el proceso, los validadores habrán mostrado sus mensajes de error y retornamos falso, haciendo que se anule el postback. Si la validación es correcta, solicitamos al usuario que confirme la acción y retornamos su decisión.
Publicado en: Variable not found.
Publicado por José M. Aguilar a las 11:40 p. m.
Etiquetas: .net, asp.net, desarrollo, nivel básico, trucos, validadores, web
Lo sé. Estoy desactualizado... pero me sigue gustando Office 2003, como supongo que ocurre a mucha gente todavía. Y de hecho, continúo creando mis documentos con Microsoft Word 2003.
Sin embargo, a pesar del largo camino que hemos recorrido juntos, aún no he sido capaz de verle sentido al modo diseño de lectura, esa extraña forma de presentar los documentos por defecto al abrirlos, o que podemos activar voluntariamente desde el menú “edición”. ¿A quién se le ocurrió esa idea? ¿Alguien la ha visto útil alguna vez? ¿Tiene, quizás, alguna utilidad oculta que no he sido capaz de intuir en todos estos años?
Bueno, en cualquier caso, podemos conseguir muy fácilmente que no utilice el diseño de lectura en el momento de abrir los documentos, accediendo al cuadro de diálogo de configuración (Herramientas >> Opciones) y desmarcando el check “Permitir el inicio en diseño Lectura”:
Sencillo, ¿eh? Desde luego, no tengo excusa para haber aguantado este odioso comportamiento durante más de un lustro…
Publicado en: Variable not found.
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