Tuesday, June 20, 2006
Ambiguedad en Interfaces
Me encontré con una técnica, que alivia este problema, y básicamente lo que se hace es un nombramiento completo, usando el nombre de la interfaz (IMiInterface1.Metodo), esto le dice al compilador que método Metodo() está siendo implementado.
using System;
public interface IMiInterface1
{
void Metodo(int x);
}
public interface IMiInterface2
{
void Metodo(int x);
}
public class MiClase: IMiInterface1, IMiInterface2
{
void IMiInterface1.Metodo(int x)
{
Console.WriteLine("Llamando al método...{0}",x);
}
void IMiInterface2.Metodo(int x)
{
Console.WriteLine("Llamando al método...{0}",x);
}
}
class App
{
public static void Main()
{
MiClase obj= new MiClase();
((IMiInterface1)obj).Metodo(1);
((IMiInterface2)obj).Metodo(2);
}
}
Debe tenerse en cuenta que cada llamada a Metodo() en el cliente necesita su respectivo Cast a la interfaz de la cual se va a usar el método.
En conclusión el nombramiento explicito, es una buen técnica, para evitar la ambiguedad entre interfaces implementadas por una clase, las cuales definen un método igual.
Sunday, June 11, 2006
TypeForwardedToAttribute
El atributo TypeForwardedToAttribute, permite básicamente poder mover los tipos definidos en nuestros assemblies, a nuevos assemblies, y que las aplicaciones clientes, simplemente sean redirecionadas a las nuevas localizaciones de los tipos que esten usando del assemblie, del cual han sido movidos estos tipos. Permitiendo de este modo, simplemente compilar los assemblies pero no las aplicaciones clientes.
Veamos esto en marcha, con un ejemplo muy sencillo.
Nuestro assemblie que llamaré "Original" tiene un tipo llamado Ejemplo:
using System;
namespace Probando
{
class Ejemplo
{
static Ejemplo()
{
Console.WriteLine("Estoy en el ensamblado original.");
}
}
}
para compilar el código anterior simplemente copienlo en su NotePad, nombren el archivo como original.cs y usando el Command Promp de VS.NET, escribimos el siguiente comando:
css /t:library original.cs
Bien con esto ya tenemos el ensamblado "original" con una clase llamada Ejemplo. Recuerden /t:library, le indica al compilador que el resultado va a ser una libreria de clases, no una aplicación ejecutable, ya que nuestro primer ensamblado no contendrá ningún método main().
Ahora creemeos un pequeño cliente que referencie el ensamblado y utilice su clase:
using System;
using Original;
namespace Aplicacion
{
class Prueba
{
static void Main()
{
Ejemplo obj = new Ejemplo();
}
}
}
Copien el código anterior al bloc de notas y nombren el archivo como prueba.cs.
En este pequeño cliente, se puede apreciar que se está creando una instancia de la clase Ejemplo, definida en el ensamblado "original".
La compilación del código anterior la pueden ejecutar de la siguiente mandera:
csc /r:original.dll prueba.cs
/r le indica al compilador que se está haciendo referencia al ensamblado original.dll, el cual contiene la definición de la clase Ejemplo, que estamos usando en nuestro cliente.
La salida de prueba.exe será: "Estoy en el ensamblado Original."
Supongamos que hemos decidido mover el tipo definido en el ensamblado "original" a otro ensamblado que llamaremos NuevaVersion. Entonces cortamos la definición de la clase Ejemplo del ensamblado "original" y la pegamos en un nueco archivo que llamaremos nuevaversion.cs. Compilamos original.cs como lo habiamos hecho antes con lo cual al ejecutar nuestro cliente obtendremos el siguiente error:
prueba.cs(2,7): error CS0246: The type or namespace name 'Original' could not be found (are you missing a using directive or an assembly reference?)
Es obvio lo anterior ya que hemos movido el tipo Ejemplo al nuevo ensamblado que ha quedado así;
using System;
namespace Probando
{
public class Ejemplo
{
public Ejemplo()
{
Console.WriteLine("Estoy en el ensamblado Nueva Versión.");
}
}
}
Copiamos el anterior código en el bloc de notas, y nombramos el archivo como NuevaVersion.cs, y se compila así:
css /t:library nuevaversion.cs
Ahora entonces que debemos hacer, para evitar recompilar la aplicación cliente y que siga funcionando aunque haya sido movido el tipo al nuevo ensamblado?
Agregamos el siguiente atributo, al ensamblado Original, con lo que el código de original.cs queda así:
using System;
using System.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(Probando.Ejemplo))]
namespace Probando
{
}
Primero noten el nuevo NameSpace agregado System.Runtime.CompilerServices; para poder hacer uso del atributo [assembly: TypeForwardedTo(typeof(Probando.Ejemplo))].
Con lo anterior, el cliente seguirá llamando al ensamblado original, pero este internamente redireccionará al ensamblado NuevaVersion para acceder al tipo Ejemplo, que previamente ya habiamos movido a ese nuevo ensamblado.
Con esto el cliente ahora recibirá el mensaje:
"Estoy en el ensamblado Nueva Versión."
Esta es una grandiosa característica, que puede servir mucho para los que se dediquen a crear librerias de clases y necesiten realizar este tipo de actualizaciones, y quieran no tocar nada en las aplicaciones cliente.
Friday, June 09, 2006
Generics
Si queremos definir un tipo, llamado stack (pila), no generico tenemos algo como esto:
class Stack {
private object[] store;
private int size;
public Stack()
store=new object[10]; size=0;
}
Ahora si lo quieren genérico, aqui está lo interesante del asunto:
class Stack
private T[] store;
private int size;
public Stack()
store=new T[10]; size=0;
}
Fijense que sería como una especie de plantilla, para la cual definiremos cualquier tipo de dato, consiguiendo una clase stack de tipos genéricos. En el caso no genérico tenemos por ejemplo un arreglo store como tipo object, lo que supone recibir cualquier tipo de datos, pero incurriendo obviamente en la costosa operación de boxing y unboxing.
Por el contrario, con nuestro modelo genérico el arreglo store, está habilitado para ser del tipo que le definamos sin incurrir en operaciones de boxing y unboxing.
Hasta el momento el ejemplo nos muestra que podemos definir tanto Clases como sus miembros de tipo genérico.
Pero ahora veamos la implementación de los métodos de ingreso y sacada de elementos de la pila en el modo no genérico:
public void Push(object x) {
if (size>=store.Size) {
object[] tmp = new object[size*2];
Array.Copy(store,tmp,size);
store = tmp;
}
store[size++] = x;
}
public object Pop() {
return store[--size];
}
Es de observar el uso de object como forma de recibir cualquier tipo de dato, pero si le pasamos tipos primitivos, incurrimos en la no bienvenida operación de box, por ejemplo, compilemos lo anterior y analicemos un poco el IL generado.
En la ventana generada gracias al ILDASM.EXE en la línea IL_000a: box, se aprecia una clara llamada a una operación de box. Ahora imaginense esto ocurriendo por cada elemento que entre a la pila de tipo primitivo, en este caso un entero, que deberá ser casteado a Object.
Ahora observermos la versión genérica mirada a través de la lupa del ILDASM:
public void Push(T x)
{
if (size>=store.Length)
{
T[] tmp = new T[size*2];
Array.Copy(store,tmp,size);
store = tmp;
}
store[size++] = x;
}
public T Pop()
{
return store[--size];
}
Alguien me puede ayudar a buscar la operación de Box que no la veo. Si señores, una ventaja de Generics vista en puro C# convertido a IL.
Como se puede observar, igualmente nuestra clase genérica puede también tener Métodos genéricos, el caso de Pop() y métodos no genéricos que reciben tipos genéricos el caso de Push().
Este ejmplo lo tome del artículo http://research.microsoft.com/projects/clrgen/generics.pdf
y quise comprobar a través del ILDASM, para ver de cerca una de las ventajas claras de Generics Types.
UNICAUCA en Imagine Cup 2006
Cuatro estudiantes con muchas ganas, se dieron a la tarea de hacer un desarrollo utilizando tecnología .NET, para cumplir con el lema del evento que reza "Imagina un mundo donde la tecnología nos permite vivir vidas mas saludables", consiguiendo un producto que basado en la Electro-Acupuntura, logran determinar el estado de salud de una persona, de acuerdo a ciertos rangos establecidos, según la información electrica recogida a través de un dispositivo hardware conectado al puerto serie, y logrando así una gran intereacción a través de una aplicación de Windows Forms, y usando también clientes Pocket y ASP.NET.
Mis más sinceras felicitaciones, y esperemos que se traigan una buena posición en la final, sino la primera.