lunes, 26 de mayo de 2008

Porque no hay referencias const en c#?

Jejejeee... los que conozcais C++ ya sabreis por donde voy... para los demás, os cuento...

En C++ se puede declarar un puntero (o una referencia, que en C++ no es lo mismo que un puntero, pero tampoco importa esto ahora) como const. Con eso le indicamos al compilador que el método que recibe el puntero no puede modificar los datos apuntados por éste.
Si la referencia apunta a un tipo simple (sin ánimo de ofender) como int o char, pues el compilador ya sabe lo que es "modificar" los datos... Por ejemplo, esto en C++ no compia:


void foo (const int* pValor)

{

*pValor = 10;

}

El compilador se queja con un claro mensaje de error (lo que ya es raro viniendo de C++): 'pValor' : you cannot assign to a variable that is const.

En el caso de que el puntero apunte a una clase... como sabe el compilador si un método modifica los datos de la clase? Imaginemos la siguiente clase:

class Perrito

{

int edad;

public:

inline void SetEdad(int anyos) { edad = anyos;}

inline int GetEdad() { return edad;}

};

Ahora imaginemos una función que trabaja con Perritos:

void foo (Perrito* pDog)

{

pDog->GetEdad();

}

Vale... la función es un poco raquítica, pero servirá... Esta funcion NO modifica el estado del objeto Perrito que se le pasa, por eso, si queremos podemos indicarlo expresamente, añadiendo const al parámetro pDog:

void foo (const Perrito* pDog) { ... }

Con esto le indicamos al compilador y al usuario de nuestra función, que foo NO modifica el estado del objeto Perrito que se le pase... Si estais compilando este código, no lo hagáis... no compila (y esta vez el mensaje ya es un mensaje típico de C++: 'Perrito::GetEdad' : cannot convert 'this' pointer from 'const Perrito' to 'Perrito &').

Entonces??? Donde está la gracia? Si añado const y el código no compila.... Bueno, no desesperemos, la gracia de todo está en que la clase Perrito es incompleta. Le falta indicar al compilador cuales son las funciones que NO modifican el estado del objeto. En concreto en este caso sabemos que la función GetEdad no modifica el estado del Perrito, mientras que la función SetEdad sí... entonces, en la clase Perrito cambiamos la definición de GetEdad:

inline int GetEdad() const { return edad;}

Ole! Ahora nuestro código si que compila: foo declara que NO modifica el estado del Perrito que recibe y, por ello el compilador sólo le va a dejar llamar a funciones declaradas explícitamente como const en la clase... si añado una llamada a SetEdad dentro de foo, entonces el compilador se me va a quejar ('Perrito::SetEdad' : cannot convert 'this' pointer from 'const Perrito' to 'Perrito &').

Bueno... así a grandes rasgos es como funciona el mecanismo de punteros (o referencias) const en C++ (luego hay ciertos añadidos como mutable, pero vamos lo básico es esto). Notar también que una función puede devolver un puntero const y entonces es quien recoge este valor devuelto el que no puede modificar el estado de los datos apuntados por el puntero.

Pues, ahora repito la pregunta: porque nunca se ha añadido este mecanismo en C#??? Para empezar nos ahorraríamos clases como la ReadOnlyCollection (mis funciones podrían recibir o devolver una const List<T>, p.ej.). De verdad... si alguien tiene alguna idea de porque nunca se ha añadido esta característica en C# que me lo diga!!

Saludos!

jueves, 22 de mayo de 2008

C# es fácil (iii): Métodos de extensión.

Bueno... vamos a seguir las entregas de C# es fácil, hablando de otras de las novedades de C# 3.0: los métodos de extensión.

En realidad los métodos de extensión no son más que la posibilidad de usar la sintaxis para llamar a un método de un objeto (vamos, el objeto.metodo() de toda la vida), para llamar a un método estático, definido en otra parte. El hecho de que el compilador nos permita usar la sintaxis objeto.metodo() nos crea la ilusión, de que el método de extensión pertenece a la clase de la cual es instancia el objeto. Pero no: el método es un simple y corriente método estático, de los de toda la vida.

Vamos a ver un ejemplo... mirad el siguiente código:

public static class Extensions


{


public static string Left(this string s, int num)


{


if (s == null) return null;


if (num <= 0) return string.Empty;


if (num <= s.Length) return s.Substring(0, num);


return s;


}


}


En él definimos una clase estática (Extensions) que contiene un método llamado Left. Fijaos en la declaración del método... en el primer parámetro lleva el modificador this. Esto lo convierte en un método de extensión. Ahora yo puedo hacer lo siguiente, desde cualquier otra parte de mi código:

string s = "Burbujas en .NET";


string s2 = s.Left(8);


Fijaos que el método que yo he definido, puedo usarlo como si fuese un método más del objeto s, que es un objeto string. Pero... que hace internamente el compilador? Pues llama al método Extensions.Left y le pasa como primer parámetro (el que tiene el this) el valor del objeto sobre el cual se ha invocado el método (en este caso mi string s).

Eso son en resumen los métodos de extensión... pero ahora unos cuantos puntos a tener claros:

  1. Los métodos de extensión NO forman parte de la clase que "extienden". Es decir, en nuestro ejemplo el método Extensions.Left, no puede acceder a los métodos protected de la clase string.

  2. Los métodos de extensión deben definirse en una clase marcada como static (y por lo tanto deben ser todos ellos static).

  3. Los métodos de extensión pueden invocarse sobre una referencia nula y no da una excepción (simplemente el método recibe null en el parámetro).

  4. Igualmente se pueden definir métodos de extensión cuyo parámetro marcado como this, sea una clase sealed. Esto nos da la ilusión de estar "extendiendo" una clase sealed.

  5. Igualmente se pueden definir métodos de extensión cuyo parámetro marcado como this sea una interface.

  6. Los métodos de extensión tambien son métodos "normales", es decir pueden llamarse en su forma... digamos tradicional. Los siguiente funcionaria:

    string s3 = Extensions.Left("Hola", 2);


  7. Los métodos de extension pueden ser genéricos, y eso vamos a verlo ahora con más detalle

Mirad el siguiente código:

public static class Extensions


{


public static string ToUpperString<T>(this T p)


{


if (p == null) return null;


return p.ToString().ToUpper();


}


}


Definimos un método de extension que funciona para cualquier objeto de cualquier tipo. Recordad que C# infiere los tipos de parámetro genéricos, o sea que las siguientes llamadas funcionan:

string s = "hola".ToUpperString();


int i = 100;


string s2 = i.ToUpperString();

En la primera llamada el compilador infiere que el tipo genérico es "string" y llama a Extensions.ToUpperString<string>(), mientras que en el segundo caso, infiere que es "int" (Int32) y llama Extensions.ToUpperString<Int32>().

En fin... eso es todo lo básico de métodos de extensión... como podeis observar no se trata más que de una treta del compilador para hacernos creer que un método está definido en una clase, cuando realmente está definido como static en otra.

Ah sí!! Y me olvidaba... ojo con el uso desmesurado de los métodos de extensión! No son y no deben ser un sustituto para la herencia o métodos normales de clase. Tienen un par de inconvenientes que hay que señalar:

  1. No pueden acceder a métodos protected de la clase que "extienden".

  2. Para poder usarlos el compilador debe poder encontrar la clase static que los contiene. Eso significa que debemos tener una referencia al assembly que contiene dicha clase static y (generalmente) el using del namespace en el cual está definida la clase.

  3. Complica el seguimiento del código por parte de terceros, porque cuando vemos objeto.metodo() no nos basta con mirar la clase (y superclases) de objeto, si no que además debemos tener presente todos los posibles métodos de extensión.

  4. Si el compilador se encuentra que puede llamar a dos métodos de extensión (vamos, que dos clases distintas me definen un método de extensión que se llama igual y ambos aceptan como parámetro this un objeto de la misma clase), el compilador se quejará con un error... exceeeeeeeeeepto si uno de los métodos de extensión es genérico, en cuyo caso el compilador a la chita callando... llamará al otro.

  5. En caso de colisión los métodos de extensión tienen menos prioridad que los métodos propios de una clase. Es decir en el siguiente código:

    public class Complex


    {

    public string ToUpperString()


    {

    return "ToUpperString de Complex";

    }


    }


    public static class Extensions

    {

    public static string ToUpperString<T>(this T p)

    {

    if (p == null) return null;

    return p.ToString().ToUpper();

    }

    }


    Una llamada a new Complex().ToUpperString(); me devolverá "ToUpperString de Complex": es decir me invocará al método propio de la clase en lugar del método de extensión.

Bueno... ahora creo que sí ya lo hemos aclarado más o menos todo... :) En resumen: usadlos con prudencia!

Saludos!

miércoles, 21 de mayo de 2008

Charla en catdotnet

Este viernes (16 de mayo) he tenido el honor de presentar una pequeña demo en una charla de catdotnet, el grupo de usuarios de .NET de la catalunya central. La charla fue en Igualada y se impartieron dos pequeñas sesiones:

  1. Em primer lugar intervine yo, y bajo el título "Qui diu que .NET 3.0 és dificil?" (¿Quien dice que .NET 3.0 es difícil?), presenté una demo práctica de unos 45 minutos en los cuales montamos una aplicación web que mostraba imágenes de una base de datos. Luego creamos una librería de servicios WCF, que se conectaba a la BBDD y devolvia las imágenes de un determinado usuario, y consumimos esta librería de servicios en una aplicación WPF.

  2. En segundo lugar fue Lleonard Del Río quien mostró una demo de Workflow Foundation y explicó sus principales características.

Ambas sesiones fueron totalmente prácticas (vamos, sin powerpoint, sólo con Visual Studio... como tiene que ser! xD).

Cuando esté el código disponible en la página de catdotnet, os aviso! :)

Saludos!

C# es fácil (ii): Usos de var

En el post anterior de C# es fácil, hemos hablado un poco sobre los tipos anónimos. Ahora me gustaría comentaros algunas cosillas de la palabra clave var. Recordemos que var viene a significar: declara la variable del tipo que se corresponda a la expresión de la derecha de la asignación.

Lo que debes tener presente de var es que:

  1. Debes declarar e inicializar la variable. Es decir: var i; no compila ya que no hay expresión de la que inferir el tipo de la variable i. Recuerda que C# es un lenguaje fuertemente tipado.

  2. var se resuelve en tiempo de compilación, nunca de ejecución. Es decir si tengo:

    class Animal { }


    class Perro : Animal { }


    y entonces hago:

    Animal animal = new Perro();


    var perro = animal;


    la variable perro se declarará de tipo Animal.

  3. No puedes devolver var:

    static var foo()

    {

    return new { X = 10, Y = 20 };

    }


    Este código no compila, con el mensaje de error (The contextual keyword 'var' may only appear within a local variable declaration). Es una lástima no poder devolver var porque esto nos impide devolver (fácilmente) tipos anónimos entre métodos. De hecho, se considera que los tipos anónimos tienen un ámbito local.

  4. Declarar una variable usando var o usando el nombre de la clase que realmente se corresponde no tiene ninguna diferencia. El código MSIL generado por el compilador es el mismo en ambos casos, o sea que usar var es una decisión personal (excepto cuando hay tipos anónimos por en medio, que entonces debe usarse sí o sí).

Saludos!

lunes, 19 de mayo de 2008

C# 3.0 es fácil (i): tipos anónimos

Bueno... con este post empezamos una (pequeña) serie de posts para desgranar las novedades de C# 3.0 (o sea, el C# de VS 2008). Veremos las novedades del lenguaje y como estas se combinan para dar soporte a LINQ... y para empezar, que mejor que tipos anónimos?

Ahora en C# podemos hacer algo como:
var i = 0;
Cuando la gente ve esto, generalmente tiene dos tipos de reacciones:
  1. A los desarrolladores con experiencia en Javascript, o otros lenguajes dinámicamente tipados. se les humedecen los ojos de la emoción de pensar que por fin C# es un lenguaje dinámicamente tipado.
  2. A los desarrolladores con experiencia en VB6 también se les humedecen los ojos, pero en este ocasión es de pánico, al pensar que una cosa parecida a Variant vuelve...
Pues no... ni C# es tipado dinámicamente, ni mucho menos, algo parecido a Variant vuelve para recordarnos nuestras peores pesadillas...
... pues entonces, que significa la palabra clave var?

En C# var significa: declara la variable del tipo correspondiente al tipo de la expresión que se halle a la derecha de la asignación...
O dicho de otro modo:
var i = 0;

es equivalente a:
int i = 0;


Y en este caso equivalente significa lo mismo, exactamente lo mismo.
Si no os lo creeis, probad a compilar esto:
var i = 0;
i = "hola";

Vereis como el compilador se quejará con un claro: Cannot implicitly convert type 'string' to 'int'

Ok, ahora que tenemos claro que significa var, para que lo han añadido al lenguaje? Uno puede pensar que es para evitar que tecleemos tanto... cierto que de var a int no van muchos carácteres, pero de var a Dictionary<MiClase, MiOtroPedazoClase>
, pues ya van unos cuantos... Pero no, nos los tiros no van por ahí... los caminos de Redmond son mucho más elevados. La incorporación de la palabra clave var viene dada por la aparición de los tipos anónimos.
Y que es un tipo anónimo? Pues como su nombre indica, una clase... sin nombre. Y como puede no tener nombre una clase, si necesito su nombre para declararla usando class? Pues porque los tipos anónimos se declaran a la vez que se construye un objeto de este tipo. Algo parecido a los métodos anónimos que los declaramos a la vez que construimos un delegado.
Para declarar un tipo anónimo y construir a la vez un objeto de este tipo de usa la palabra clave new (eso tiene al menos lógica, no? xD).
Observad el siguiente código:

string data = "Francesc Eiximenis";
var eiximenis = new { Nombre = data.Split(' ')[0], Apellido = data.Split(' ')[1] };

Estamos creando un tipo anónimo, con dos propiedades (Nombre y Apellido) y a la vez estamos creando (e inicializando) un objeto de este tipo, y asignándolo a la variable eiximenis. Fijaos en el uso de var para declarar la variable... puesto que si no seria imposible hacerlo (no hay ninguna clase Eiximenis!).
El uso clásico de los tipos anónimos consiste en casos como este: queremos agrupar dos o tres datos en un objeto, pero nos da pereza crear una clase nueva... Pues nada... solucionado!

Pero vamos a hacer cosillas un poco... más divertidas y es que aunque Eiximenis por si sólo ya da para mucho, que pasa si quiero crear pongamos una lista de objetos de este tipo y luego recorrerla? Vamos a verlo...
List<string> _strs = new List<string>();
_strs.Add("Francesc Eiximenis");
_strs.Add("Ramon Llull");
_strs.Add("Bernat Metge");
ArrayList escritores = new ArrayList();
foreach (string s in _strs)
{
string[] tokens = s.Split(' ');
escritores.Add(new { Nombre = tokens[0], Apellido = tokens[1] });
}

Con este código creo tres objetos de mi tipo anónimo y los añado a un ArrayList... Y como se que tengo tres objetos de un tipo anónimo, y no tres objetos de tres tipos anónimos (con las mismas propiedades?)... pues porque el compilador de C# no es tonto del todo y es capaz de reaprovechar tipos anónimos si declaramos dos o más objetos anónimos con las mismas propiedades... cosa que veremos que es realmente interesante.

Ok... pues eso: tengo el ArrayList escritores con tres objetos, y puedo asegurar que los tres son del mismo tipo anónimo... y ahora quiero recorrer este ArrayList...
foreach (var escritor in escritores)
{
Console.WriteLine("Nombre: " + escritor.Nombre);
}

Ajá!!! Listos habreis pensado... pues no :( Este código que acabo de poner... no compila. Y por que? Pues porque var se resuelve en tiempo de compilación, no en tiempo de ejecución... y aunque la ArrayList escritores contenga objetos de mi tipo anónimo, la clase ArrayList define que su enumerador devuelve Object, así que la variable escritor es una referencia de tipo object. Encima como el tipo es anónimo no puedo hacer un cast de escritor al tipo real porque no tiene nombre!
Estamos en un callejón sin salida? Por los pelos... pero no.

La solución pasa por generar una clase que me defina su enumerador al tipo anónimo... en definitiva en lugar de una ArrayList, usar una List<T>, siendo el parámetro genérico T nuestro tipo anónimo...

---- un incómodo silencio va aquí ----

... valeeeeeeeeeeeeee... me habeis pillado!!! Si no tenemos nombre de clase, como vamos a hacer:



List<T> escritores; // Que pongo en lugar de T si no tengo nombre de clase?

Pues por suerte tenemos una posibilidad de hacerlo, aprovechandonos del hecho de que C# es capaz de inferir los tipos genéricos de un método.
Recordad que si tengo el siguiente método:
public static Type TypeOf(T type)
{
return type.GetType();
}

Entonces, gracias a la inferencia de tipos genéricos, estas dos llamadas son equivalentes:
TypeOf<int>(10);
TypeOf(10);


La primera llamada es la tradicional (pasamos el tipo genérico). En la segunda llamada el tipo genérico se infiere a partir del tipo del parámetro.

Entonces si definimos este método:
public static List<T> ListOfType(T type)
{
return new List<T>();
}

Este método, dado un parámetro de un tipo nos devuelve una referencia a una List preparada para contener referncias de este tipo:
List<string> s = ListOfType<string>(string.Empty);

Y aprovechandonos de la inferenncia de tipos genéricos:
List<string> s = ListOfType(string.Empty);

Y si usamos var:
var s = ListOfType(string.Empty);

Todas estas llamadas son equivalentes, y en los tres casos s es una List<string>
. Nos vamos acercando, porque entonces...

var escritores = ListOfType(new { Nombre = string.Empty, Apellido = string.Empty });
foreach (string s in _strs)
{
string[] tokens = s.Split(' ');
escritores.Add(new { Nombre = tokens[0], Apellido = tokens[1] });
}
foreach (var escritor in escritores)
{
Console.WriteLine("Nombre: " + escritor.Nombre);
}

Ya lo tenemos!!!! Fijaos que el truco es declarar la variable escritores, pasándole un objeto de mi tipo anónimo... y eso funciona porque, si os acordáis, os comenté que el compilador de C# reaprovecha el tipo anónimo. Por suerte, porque si cada vez me crease un tipo anónimo distinto esto no funcionaria (el primer Add sobre escritores me petaría con un error de conversión).

Saludos!!!

miércoles, 14 de mayo de 2008

Culturilla: Covarianza en genéricos

Este es el primer post de una serie (espero que larga) de posts de culturilla: cada cuando pueda iré publicando posts con información sobre cosas interesantes (a mi criterio, claro :P) sobre .NET y que no está de más saber... vamos, como su nombre indica, un poco de culturilla sobre .NET.
Y para empezar, un tema que tarde o temprano todo el mundo se termina planteando: son covariantes los genéricos de .NET? O, lo que es lo mismo: si los perros son animales, entonces las listas de perros son listas de animales?

Todos estamos acostumbrados a hacer algo como esto:

class Animal { }

class Perro : Animal { }

class Program

{

static void Main(string[] args)

{

Domestica(new Perro());

}

static void Domestica(Animal a)

{

}

}


Es decir, pasar un Perro a un método cuya firma especifica que espera un Animal. Esto además es lógico puesto que si la clase Perro deriva de la clase Animal, entonces todos los perros son animales.
Dicho esto, uno esperaria entonces que el siguiente código funcionase también:

class Animal { }

class Perro : Animal { }

class Program

{

static void Main(string[] args)

{

DomesticaVarios(new List<Perro>());

}

static void DomesticaVarios(List<Animal> a)

{

}

}


Pero si compilamos este código, veremos que no compila. Visual Studio se nos quejará con un mensaje parecido a: cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List'

Así que, aunque todos los Perros sean Animales, resulta que una lista de Perros NO es una lista de Animales. Es por eso que se dice que los genéricos en .NET no son covariantes.

Una vez que hemos comprobado esto, la pregunta es si esto es lógico o no... Hombre, a simple vista podríamos decir, que es lógico: de la misma manera que los métodos que trabajan con Animales funcionan con Perros, los que trabajen con una lista de Animales, deberían hacerlo con una lista de Perros... Entonces por que no funciona?

Primero por como funcionan los genéricos: List<Animal> y List<Perro> son dos clases completamente distintas, que tienen la misma relación que List<Animal> y List<Florecilla> por decir algo.
Otra razón que podríamos argumentar es para mantener la coherencia de la lista, si pudiesemos pasar una
List<Perro> a un método que espera una List<Animal> nada impide hacer esto:

static void Domestica(List<Animal> a)

{

a.Add(new Gato());

}


(Suponed que la clase Gato deriva de Animal).

En este caso, tenemos un List<Perro>... donde uno de sus elementos no es un Perro!!! No se si nunca habeis juntado varios perros con un gato, pero os puedo decir que se arma una de buena!

Este segundo argumento podria ayudarnos a entender un poco la no covarianza de genéricos en .NET... y digo solo un poco, porque para liar las cosas... los arrays si que son covariantes. Así, que si todavía quereis ver lo que pasa cuando se junta un gato con varios perros, siempre podeis hacer lo siguiente:

static void Main(string[] args)

{

Perro[] p = new Perro[10];

Domestica(p);

}

static void Domestica(Animal[] a)

{

a[1] = new Gato();

}


Y ahora la pregunta es... que hay en el array p, después de llamar a Domestica()????
Pues la respuesta es que el programa casca que da gusto con una ArrayTypeMismatchException, así que bueno... seguimos sin poder juntar perros con gatos :)

Saludos!

miércoles, 7 de mayo de 2008

Bienvenidos!

Hola a todos! Bienvenidos a este, mi blog sobre desarrollo en .NET.

Aquí iré hablando de todo lo que me vaya pasando por la cabeza... relacionado con el desarrollar apliaciones usando .NET.

Antes de nada... quien soy yo? Sí... es una buena pregunta que suelo formularme (por lo general un domingo por la mañana, medio resacoso)... pues me llamo Edu, y trabajo en raona, donde ostento el rimbombante título de Technology Specialist. Me apasiona el desarrollo de aplicaciones, en concreto en C++ y en .NET que es con lo que me gano la vida. También me encanta XNA, pero con él no me gano la vida, jejejeee...
En el ámbito personal... vivo en igualada y me encanta la literatura fantástica de calidad, la música heavy hecha por grupos frikies y los juegos de rol de lápiz y papel... :) Si después de esto aún tenéis curiosidad para saber más sobre mi... daos un paseillo por el brutal blog dels kmarades, donde podreis ver lo que hago junto con los colegas en mi tiempo libre...

También, y ya puestos a hacer propaganda, podeis pasaros por el blog de "World of Fighters", un proyecto en el que participo, para realizar un juego on-line de cartas a lo magic, que revolucionará internet... vuestros comentarios serán bienvenidos!! xDDDDD

Bueno... vaya tostón de primer post, eh?? ;-)
Nada, espero que el blog os vaya gustando, y que nos vayamos viendo por aquí...

Saludos!