1. Lanzamiento
Visual Studio 2008 y .NET framework 3.5 serán lanzados oficialmente juntos el próximo febrero. Sin embargo, estará disponible para desarrolladores a finales de noviembre de 2007.
Afortunadamente, estarán disponibles las versiones Express de C#, VB, C++ y Web, así como las Profesionales (¡con soporte de testeos unitarios!), Estándar y ediciones de equipos de desarrollo. La novedad será Visual Studio 2008 Shell, de carácter gratuito, que permitirá crear lenguajes y herramientas de desarrollo más verticalizadas.
Daniel comenta también que bajo Windows Vista, VS2008 será espectacular, e incluirá mejoras para la depuración de múltiples hilos. Ya no hay excusa para quedarnos con WXP ;-P
2. Compatibilidad hacia atrás
.NET framework 3.5 continúa la línea iniciada por Fx3.0 en cuanto al mantenimiento del CLR. Por tanto, y dado que lo único que hace es añadir ensamblados a las librerías presentes con las versiones 2.0 y 3.0 del framework, las aplicaciones actuales no se verán afectadas. Eso sí, necesitará los Service Packs 1 de ambas plataformas.
3. Generación multiplataforma
Visual Studio 2008 incluye la capacidad de crear proyectos para múltiples plataformas .NET, es decir, la 2.0, 3.0 y 3.5, desde el mismo entorno. Por tanto, no será necesario tener VS2005 instalado para generar ensamblados para .NET 2.0.
4. Multitud de novedades en C# 3.0 y VB9
Propiedades automáticas, delegados "relajados", inicializadores de objetos, inferencia de tipos, tipos anónimos, métodos de extensión, funciones lambda y métodos parciales, entre otros.
Pero no sólo eso... dado el punto 3 (generación multiplataforma), podremos usar estas nuevas características de nuestros lenguajes favoritos y generar para .NET 2.0.
5. LINQ
Se trata de una de las grandes revoluciones que nos aportará este nuevo conjunto de herramientas. Language INtegrated Query es un nuevo método de acceso a datos totalmente integrado en nuestro lenguaje habitual y de una forma muy independiente de la fuente de donde provengan (colecciones, XML, motores de bases de datos, etc.).
6. Novedades para ASP.NET
Visual Studio, así como el nuevo framework, ya incluirán ASP.NET AJAX de serie, así como 3 nuevos controles (ListView, DataPager y LinqDataSource). Además, el IDE ha sido muy mejorado e incluye soporte para intellisense y depuración de Javascripts, ¡también para ASP.NET 2.0!, y un nuevo diseñador que permite anidar páginas maestras.
7. Para el desarrollo en cliente
VS2008 incluirá nuevas plantillas de proyectos, así como un diseñador para WPF integrado con soporte para la comunicación WPF-WinForms. También se ha añadido el soporte para Firefox de la tecnología ClickOnce y XBAP (XAML Browser Applications).
8. Para el desarrollador de Office
Se ofrece soporte total para la personalizaciones (customisations) de Office 2007, así como para las plantillas de Office 2003.
9. Para desarrollo en servidor
Se han incluido nuevas plantillas para WCF y WF, y se han introducido mejoras interesantes en el primero, como el modelo de programación HTTP (sin SOAP) o serialización JSON.
10. Para el desarrollo en dispositivos móviles
Hay decenas de nuevas características, como el soporte para las versiones compactas de LINQ y WPF, o, a nivel de IDE, Unit Testing for Devices.
11. (punto extra) Código del framework
Pues sí, como ya es sabido, podremos depurar nuestras aplicaciones siguiendo el rastro por el interior de las clases y métodos del framework
Más información en la fuente: The Moth.
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
Sin embargo, para un desarrollador habituado a arrastar y soltar, estas dos simples operaciones suponen demasiado esfuerzo como para considerar el uso de esta librería ;-), de ahí que me decidiera a crear NiftyDotNet.
NiftyDotNet es un componente para ASP.NET, escrito en C#, que encapsula todos los archivos y lógica necesarios para hacer funcionar Nifty Corners Cube en nuestras webs de una forma aún más fácil, simplemente arrastrando y soltando el control sobre nuestros webforms y ajustando unos sencillos parámetros, como el tamaño del borde o la selección de elementos a los que afectará.
Para que quede más claro, ahí va un ejemplo: observamos un formulario web en tiempo de diseño con un div central (que tiene un id='box1') y un control Nifty que afecta al mismo. A la derecha podemos ver también las distintas opciones de parametrización del control (puedes hacer clic sobre la imagen para ampliarla un poco):
Y continuación una captura con la página en tiempo de ejecución:
Como se puede ver, las esquinas del div central han sido redondeadas por el componente sin usar imágenes externas, sólo javascript no intrusivo. Increíble, ¿no? ;-)
El proyecto, publicado bajo GPL, se distribuye en tres paquetes, a elegir según el gusto y pretensiones del usuario:
- Un ensamblado listo para ser utilizado en nuestros proyectos, o para añadirlo a la caja de herramientas (Toolbox) del entorno de desarrollo.
- Una web de demostración, en la que podéis haceros una idea de la utilidad del componente. No hay una demo on-line debido a que en los servidores donde se encuentra no existe esta posibilidad, cuando encuentre un alojamiento apropiado la pondré para que pueda verse en vivo y en directo.
- La solución VS2005 con el código fuente completo de ambos.
En la actualidad hay ya una versión bastante estable y depurada (a falta de comentarios de usuarios, claro!). Además, ha sido testeada tanto con Microsoft .Net Framework como con Mono, lo cual es siempre una buena noticia.
Por supuesto estaré encantado de recibir vuestras aportaciones, sugerencias, colaboraciones o comentarios de cualquier tipo.
Enlaces:
- Web del proyecto NiftyDotNet (alojado en Google code).
- Blog del proyecto, en inglés (o algo parecido ;-))
Publicado por José M. Aguilar a las 11:50 a. m.
Etiquetas: asp.net, c#, componentes, css, diseño, niftydotnet, redondear esquinas, 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');
- 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
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:
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.
Publicado por José M. Aguilar a las 8:10 p. m.
Etiquetas: .net, asp.net, c#, desarrollo, programación