Pensemos por un momento cómo podemos construir en C# 1.0 o 1.1, una clase contenedora de datos que se pueda utilizar con la construcción foreach para iterar por los elementos que contiene...
Para empezar la clase debe implementar la interface IEnumerable. Esto tampoco supone mucho trabajo ya que, al fin y al cabo, sólo se debe implementar un método, GetEnumerator(). Ahí se empieza complicar la cosa un poco ya que este método devolverá un objeto que implementa la interfaz IEnumerator y que nosotros debemos definir también. La implementación de esta clase es algo más compleja y además debe mantener una referencia al objeto enumerable original.
Ya de por sí, como podemos comprobar, se trata de algo bastante tedioso. Pero es que además de eso en algunos casos es algo muy complicado de hacer. Por ejemplo, si queremos implementar una clase enumerable que contenga los nodos de una jerarquía (un árbol de carpetas, por ejemplo) y que, enumerándolo con foreach, nos devuelva por el orden correcto de visualización todos los elementos del árbol en todos los niveles. Esto ya es algo más complicado y farragoso: hay que meterse en procedimientos recursivos y coordinar las llamadas a Current(), MoveNext() y Reset(). No digo que no se pueda hacer ni mucho menos. Sólo digo que es más complicado todavía.
Los Iteradores de C# 2.0: facilitándonos la vida.
Los nuevos iteradores de C# 2.0 nos permiten crear clases iterables con foreach pero sin apenas esfuerzo y complicación. Además permiten iterar por colecciones de elementos más complejas que una simple lista de elementos, como por ejemplo el árbol jerárquico del que hablaba más atrás.
Imaginemos una clase Contenedor que almacena una lista de objetos de tipo indeterminado de momento (aquí haremos uso de tipos genéricos que comenté en un post hace unos días). La definiremos sin especificar qué tipos de elemento albergará y así la hacemos más útil. Para coseguir que sea enumerable implementará la inerfaz IEnumerable, como antes, pero ahora en lugar de crear un objeto enumerador auxiliar emplearemos una nueva construcción. Veámoslo:
public class Contenedor : IEnumerable { private Tipo[] datos; //Aquí se almacenan los datos de la clase
public Add(Tipo dato)... //Habría que implementarla, lo dejo fuera por claridad.
public IEnumerator GetEnumerator() //Aquí se implementa la interfaz { foreach (Tipo dato in datos) { yield dato; } } }
Tal y como se observa en el código anterior, la novedad estriba en el uso de la nueva palabra clave yield. Ésta devuelve el elemento seleccionado y "congela" la ejecución del código (en este caso del bucle que la contiene) hasta que se realiza una nueva llamada al enumerador resultante, momento en el cual el bucle continua. Este es el efecto conseguido, en realidad el compilador hace el trabajo sucio por nosotros ahorrándonos decenas de líneas de código. Muy útil ¿verdad?.
De hecho yield se puede usar de modo más creativo aún para hacer cosas, antes complicadas, de manera sencilla. yield se puede usar con cualquier método que devuelva un IEnumerator (lo normal), o incluso si devuelve un IEnumerable. esto abre nuevas posibiliddes. Por ejemplo, imaginemos que queremos iterar por un subconjunto de datos de una clase contenedora. Podemos conseguirlo devolviendo un IEnumerable añadiéndole este método a la clase anterior:
public IEnumerable<Tipo> SubConjunto(int desde, int hasta) { while (desde < hasta) { yield datos[desde++]; } }
Como vemos lo único que se hace es recorrer los elementos desde el elemento inicial al final devolviéndolos mientras tanto y "deteniendo" la ejecución del bucle con yield hasta la siguiente llamada a la enumeración. Es infinitamente más fácil que antes.
El interesado podrá encontrar un estupendo artículo a fondo sobre este tema en el número 7 de DotnetMania, de Septiembre de 2004. Además de explicarlo a fondo los autores (Miguel Katrib y Mario del Valle), en un alarde de virtuosismo, se curran un equivalente a esta técnica hecha con multi-subproceso, y que funciona en la versión actual de C#. Muy recomendable. El código asociado con esta implementación se puede descargar desde aquí.
Dentro de la iniciativa Desarrolla con MSDN de Microsoft Ibérica, el próximo día 2 de diciembre de 2004, jueves, en Santiago de Compostela, impartiré formación sobre desarrollo de aplicaciones Web con ASP.NET.
El curso es gratuito. Durará toda la mañana y nos hará falta el tiempo, así que por favor sed puntuales (9:00 en punto) los que os apunteis ;-)
En esta sesión exploraremos el entorno de desarrollo de ASP.NET y los conceptos más importantes para construir aplicaciones web escalables y robustas. Los conceptos aprendidos nos permitirán desarrollar el frontal web principal y el cliente móvil de la aplicación de ejemplo MSDN Video.
La agenda aproximada es la siguiente:
- Modelo de ASP.NET e IIS
- Web Forms
- Controles de usuario
- Mobile Web Controls
- Sesión y caché
- Configuración e instalación
- Seguridad
Para anotarse sólo hay que hacerlo a través de este enlace, usando el icono que hay en el lateral. En él encontrareis más información, la ubicación exacta y un plano de cómo llegar. Para poder asistir es indispensable anotarse.
Nos veremos allí.
Cuando hacemos referencia a una clase o un tipo desde nuestro código podemos emplear una ruta completa de espacios de nombres para llegar hasta él. Por ejemplo, si escribimos System.Console.ReadLine(); se busca la clase Console dentro del espacio de nombres System que está en la raíz de todos los espacios de nombres (es decir, System se encuentra en la parte más alta de la jerarquía de espacios nombres junto a otros como Cryptography, etc... y los que nosotros mismos definamos o agreguemos a un proyecto).
¿Qué pasa sin embargo si escribimos esa misma línea dentro de una clase que está contenida dentro de un espacio de nombres llamado también System?, por ejemplo:
namespace MiEmpresa.MiBiblioteca { namespace System { class MiClase { System.Console.ReadLine(); } } }
El resultado será que la búsqueda de la clase Console comienza en el espacio de nombres actual y sólo cuando se recorra y no aparezca (porque pudiera aparecer si la hemos definido nosotros) se buscará desde el raíz que es el que verdaderamente nos interesaba.
En C# 2.0 se puede evitar esto con el nuevo cualificador que sirve para indicar la raíz y que se denota mediante el operador "::", es decir, dos símbolos de dos puntos seguidos. Así en el ejemplo, bastará con escribir:
::System.Console.ReadLine();
para que se busque desde la raíz directamente.
Cuiroso ¿verdad?
Una de las grandes novedades de la versión 2.0 de la plataforma .NET son sin duda los tipos genéricos. Esta nueva característica de los lenguajes Visual Basic .NET y C# y sus correspondientes compiladores, permite escribir código genérico que, a la hora de compilarse se transforma en código específico para un tipo de datos.
Ello permite crear código de métodos, estructuras, clases e incluso interfaces sin preocuparnos por los tipos de datos que vamos a utilizar a la hora de la verdad.
Hasta la fecha cuando queríamos escribir código genérico que pudiese trabajar indistintamente con dos o más tipos de datos no nos quedaba más remedio que utilizar el tipo 'object' que es la raíz de todas las clases. Con los nuevos Generics de .NET 2.0 ya no es necesario, y nuestro código puede disfrutar de la robustez de tipos concretos sin tener que comprometerse por adelantado con ninguno.
El ejemplo clásico de este concepto (lo usan casi todos los autores en los artículos que he visto) es el de la clase 'Pila' ('Stack' en inglés). La clase Stack se usa para almacenar objetos o tipos básicos de forma LIFO (Last In First Out), es decir, se almacenan elementos y luego se pueden recuperar empezando siempre por el último que se haya introducido. Independientemente del tipo de objetos que se almacenen en la pila el código para gestionarlos es el mismo. Si todos los objetos que se almacenarán son del mismo tipo es una ineficiencia utilizar el tipo object, siendo mejor usar tipos concretos. La solución clásica sería usar object o bien crear diversas versiones sobrecargadas de los métodos de la pila que tomasen los diferentes tipos de objeto que ésta puede manejar. Un rollo.
En C# 2.0, por ejemplo, la clase pila se definiría así:
public class Stack<CualquierTipo> { private CualquierTipo[] m_items; public CualquierTipo Pop() {.....} public void Push(CualquierTipo data) {....} }
Fíjate que si la clase la estuviéramos definiendo para, por ejemplo, números enteros, la definición sería la misma solo que en donde ahora hemos puesto 'CualquierTipo' pondría 'int'. Es la única diferencia ya que lo demás (el código que no hemos escrito), sería idéntico.
El fragmento anterior define una pila genérica. Para utilizarla debemos hacerla concreta, esto es, debemos crear una instancia de la misma diciendo qué tipo de datos queremos utilizar con ella. En código esto se traduce en que, por ejemplo, si queremos crear una pila de números enteros, tendríamos que escribir:
Stack<int> pila = new Stack(<int>); pila.Push(5); pila.Push(1); int x = pila.Pop();
Como vemos se declara la pila indicando entre signos de menos y mayor el tipo que queremos usar para sustituir al genérico. Luego la podemos usar directamente como si siempre hubiera estado definida con ese tipo de datos. Es de lo más cómodo y flexible.
Por supuesto se puede usar cualquier tipo a la hora de definir una instancia concreta. Por ejemplo si tenemos una clase 'Usuario' y queremos gestionar una pila de usuarios sólo tenemos que crear una instancia específica así:
Stack<Usuario> pila = new Stack(<Usuario>);
También es posible utilizar varios tipos genéricos en una definición, sólo hay que separarlos con comas dentro de la pareja < y >.
Este artículo de Juval Lowy, que MSDN ha tenido la gentileza de traducir al castellano, explica con todo lujo de detalles esta característica, creando varios ejemos interesantes. Lo recomiendo encarecidamente para aprender más. La sintaxis para Visual Basic difiere un poco pero el concepto es el mismo. en el artículo se han incluido ejemplos e instrucciones en los dos lenguajes.
Siguiendo la estela marcada ayer, los próximos días me dedicaré, sin prisa pero sin pausa, a comentar algunas novedades interesantes que trae la plataforma .NET 2.0, C# 2.0 y Visual Studio .NET 2005.
Hoy le toca el turno a los métodos anónimos. Éstos están pensados para simplificar la definición de manejadores de eventos cuando las tareas a realizar son simples.
Imaginemos que queremos asociar una única línea de código (o dos) como acción a realizar cuando se produzca un evento. Por ejemplo, que cuando se pulse un botón se muestre un mensaje de saludo. Lo que debíamos hacer hasta ahora era declarar una función del tipo adecuado y asociarla con un nuevo delegado al evento. Por ejemplo:
private void button1_Click(object sender, System.EventArgs e) { MessageBox.Show("Hola"); }
button1.Click += new System.EventHandler(button1_Click);
Esto hace que al pulsar el botón se vea el saludo "Hola". Sin embargo es un poco engorroso para tan poca acción tener que crear una función como esta ¿no?. Para evitarlo los métodos anónimos vienen al rescate.
Un método anónimo nos permite definir una fución de manera implícita, sin necesidad de declararla. En el ejemplo anterior un método anónimo sustitutivo sería el siguiente:
button1.Click += delegate{ MessageBox.Show("Hola"); };
Raro ¿no?. La única diferencia es que ahora no nos hace falta definir una función, lo hace automáticamente por nosotros el compilador a la hora de crear el ejecutable. Dependiendo del tipo de evento que estemos asignando el compilador ya deriva de forma automática los parámetros que necesita el método anónimo así como el tipo de delegado que se debe crear (en este caso System.EventHandler). Una "virguería" vamos.
En algunos tipos de eventos, como la pulsación del ratón y otros, necesitaremos acceder a los parámetros de éste para conocer cierta información. En este caso lo único que debemos hacer es declarar los argumentos tras la palabra 'delegate', así:
button1.Click += delegate(object sender, EventArgs e){ MessageBox.Show(e.ToString()); };
Como vemos al declarar el método anónimo indicando en el delegado qué tipos de parámetro se necesitan y otorgándoles un nombre es posible hacer uso de ellos sin problemas desde el método anónimo.
Desde luego es muy interesante para ciertas circunstancias.
Cuando creamos una página ASP.NET con Visual Studio 1.x lo que obtenemos es un archivo '.aspx' con el HTML y etiquetas básicas de servidor para enlazar datos y para directivas de páginas, y por otro lado un archivo '.cs' o '.vb' que contiene una clase que implementa el código de la página en cuestión. A este modelo de programación se le conoce como "Code-Behind" que se podría traducir como "Código por detrás". El símil es bastante bueno ya que un usuario llama a la página ASPX pero en realidad se ejecuta por detrás y de forma transparente el código de la clase "Code-Behind". En la clase code-behind, además, se incluyen las declaraciones de los distintos controles de la página y se asignan también los manejadores de sus eventos entre otras cosas. De hecho la página final hereda de la clase code-behind, que en última instancia hereda de la clase Page.
En ASP.NET 2.0 y Visual Studio 2005 esto ha cambiado. Ahora el término nuevo a conocer es "Code-Beside" que podría traducirse como "código al lado". Esta característica está basada en el concepto de calse parcial, nuevo en .NET 2.0, que permite que una misma clase esté definida de forma parcial en múltiples archivos (hasta hora una clase sólo podía estar definida en un archivo). De este modo se puede definir parte de una clase en un archivo físico, algunas de sus propiedades en otro, sus métodos en otro, etc... La utilidad de esto está pensada sobre todo para herramientas de creación de código, para facilitar la regeneración de código parcial de una clase y para que varios programadores puedan trabajar al mismo tiempo en partes diferentes de una misma clase.
En el caso de las páginas ASP.NET 2.0, Visual Studio 2005 aprovecha esta característica para definir parte de la clase de la página ASPX en el propio archivo '.aspx', que ahora contiene definiciones de controles y eventos, y el código que gobierna su funcionamiento en un archivo aparte por medio de una clase parcial.
Esta característica se gobierna desde los atributos del modificador 'Page' de la página, del mismo modo que ahora se hace con las clases code-behind. La sintaxis ha cambiado sin embargo y ahora es análoga a esta:
<%Page compilewith="MiPagina.aspx.cs" classname="MiEspacioDeNombres.MiClase" %>
La clase 'MiClase' se define como una clase parcial (palabra clave partial en C# y Expands en VB.NET) y contiene sólo el código que gobierna el funcionamiento de la página.
Las ventajas de este nuevo modelo de trabajo (por cierto, code-behind no está soportado por VS.NET 2005) son varias, y cabe destacar una mayor sencillez de código y una mayor separación real entre interfaz y código. Por otra parte existen desventajas, claro. Las más importantes son que ahora el modelo no se rige tan claramente como antes por los principios de la programación orientada a objetos (es menos intuitivo desde este punto de vista) y que es más difícil de mantener sincronizado el código con la interfaz si tocamos en donde no debemos. De todos modos como VS.NET 2005 todavía está en beta puede que esto cambie en la versión definitiva.
Ayer intenté actualizar mi nuevo TabletPC con el Service Pack 2 de Windows XP. Dificil misión.
Como es sabido, Microsoft iba a sacar al mercado como producto independiente una nueva versión del sistema operativo Windows XP para TabletPC. Al final decidieron fusionarlo con el SP2 para Windows XP. Por lo tanto si instalamos el SP2, aparte de aumentar mucho la seguridad del sistema, si tenemos un TabletPC obtendremos también la nueva versión del sistema operativo: TabletPC 2005.

Entre las mejoras de la versión 2005 están un panel de entrada de datos muy mejorado respecto al anterior, mejor y más potente integración con Office, mejoras en la conversión de escritura a texto, más capacidades para desarrollo de aplicaciones, etc... Puedes ver la lista completa aquí.
El caso es que al intentar instalar el SP2 en castellano sobre mi TabletPC en castellano no había manera: el instalador porfiaba en decir que los idiomas del SP2 y el sistema no coincidían. Nada que hacer. Llamé al soporte técnico de Acer (la marca de mi equipo), y no se enteraban ni de que les estaba hablando. Incluso me dijeron que Microsoft todavía no tenía preparada la nueva versión y que los llamara a ellos para que me dijeran cuándo la tendrían (Alucinante. No sirve de nada el soporte).
El caso es que al final decidí probar el SP2 en inglés, aún a riesgo de que el sistema empezase a funcionar en ese idioma y despejase el castellano. Increiblemente funcionó a la perfección, y la interfaz nueva sigue estando en castellano. Imagino que en el fondo Windows XP para TabletPC debe de ser una versión multi-idioma de XP con base en inglés e interfaz en otro idioma, y por eso pasa esto.
Conclusión: Si tienes un TabletPC hazte con una copia del Windows XP SP2 en inglés e instálala rápidamente. No te arrepentirás.
Cuando lanzamos un nuevo hilo de ejecución en una aplicación, éste puede ejecutarse en segundo plano o no. Depende del valor que tenga la propiedad IsBackground del objeto Thread asociado.
Los subprocesos en segundo plano son idénticos a los subprocesos en primer plano, pero los subprocesos en segundo plano no impiden que un proceso finalice. Es decir, un proceso en primer plano permanece "asociado" al proceso que lo lanzó y no permite que éste termine hasta que él mismo a su vez haya terminado su ejecución. Por el contrario, cuando hay procesos en segundo plano (IsBackground = true) y terminan todos los procesos en primer plano que pertenecen a un determinado proceso, la CLR finaliza el proceso llamando al método Abort de todos los subprocesos en segundo plano que todavía se estén ejecutando.
En resumen: si tenemos un proceso en segundo plano debemos asegurarnos de que termina antes de que lo haga el proceso principal que lo lanzó o se suspenderá automáticamente y no terminará su trabajo. Si cambiamos su propiedad IsBackground para que sean de primer plano (false) entonces o habrá problema.
Todo esto viene a raíz de lanzar métodos de forma asíncrona. Cuando se usa la invocación asíncrona de algún método (con BeginInvoke) la CLR genera automáticamente un subproceso paralelo en segundo plano, y si no tenemos cuidado podemos cancelarlo inadvertidamente al cerrar el proceso principal (cerrando la aplicación, por ejemplo).
NOTA: Gracias a Germán Castro, programador de krasis, por darse cuenta de esto último y comentármelo.
El otro día ayudando a una persona surgió la necesidad de que una aplicación se conectara y se desconectara de Internet automáticamente utilizando un módem estándar, a través de la línea telefónica. La aplicación estaba escrita en Visual Basic 6 (nada de .NET). Tras darle vueltas un rato llegué a esta solución que pasa por usar la API de Windows para conseguirlo:
' Declaraciones de la API Private Declare Function InternetAutodial Lib "wininet.dll" _ (ByVal dwFlags As Long, ByVal dwReserved As Long) As Long Private Declare Function InternetAutodialHangup Lib "wininet.dll" _ (ByVal dwReserved As Long) As Long
Private Const INTERNET_AUTODIAL_FORCE_ONLINE = 1 Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2
Con ellas, para conectarse a Internet de forma automática con un módem, sin intervención del usuario, sólo hay que escribir:
InternetAutodial INTERNET_AUTODIAL_FORCE_UNATTENDED, 0
Para desconectarse al terminar de hacer lo que necesitamos en la Red sólo hay que escribir:
InternetAutodialHangup 0
Si queremos conectarnos pero pedir confirmación al usuario antes la instrucción precisa es:
InternetAutodial INTERNET_AUTODIAL_FORCE_ONLINE, 0
Con esto se solucionó el problema.
Es muy fácil pasar estas declaraciones de la API a .NET y hacer uso de las mismas desde aplicaciones en C# o VN.NET.
En ocasiones hay que realizar transformaciones más o menos complejas con los datos de un origen de datos antes de enlazarlos a un control. Lo mejor en estos casos es crear un método que devuelva una cadena como resultado, en una clase accesible desde la página que haga las operaciones. Luego se lo puede llamar desde el código de enlazado (<%# %>) para no hacerlo innecesariamente largo o complejo.
Sin embargo y en contra de lo que pudiera parecer, no llega con que la clase con las utilidades se encuentre dentro del mismo espacio de nombres que el resto de la aplicación, puesto que el código de enlazado a datos se ejecuta dentro de su propio espacio de nombres, específico para la página actual. Para asegurarnos de que al enlazar los datos se ecuentran esas funciones auxiliares podemos hacer tres cosas:
1.- Si la función de transformación estamos seguros de que sólo la vamos a usar desde la página actual bastará con incluirla dentro del archivo de código "CodeBehind" para poder usarla directamente por su nombre, sin necesidad de calificadores previos.
2.- Poner la ruta completa dentro del espacio de nombres al método a utilizar. Por ejemplo: MiEspacioDeNombres.ClaseAuxiliar.MetodoEspectacular();
3.- Usar la directiva <%@ Import %> para indicar el espacio de nombres en el que se encuentra el método: <%@ Import Namespace="MiEspacioDeNombres" %>
Seguro que, aunque básico, a más de uno este consejo le resulta útil.
|
|
Copyright © 2010 José Manuel Alarcón Aguín. All rights reserved.
|
|