La informática es una tecnología que nos afecta a todos de forma directa o indirecta por eso estar informado sobre lo que pasa en el mundo de los ordenadores es algo que muchos consideran interesante y otros hasta importante. Desafortunadamente estar informado en estos temas no es siempre facil ya que cada noticia puede venir "contaminada" de terminología propia del ramo que no todos consiguen entender y es muy facil caer en malos entendidos. Para empeorar la situación, esta información es bastante escasa en su riqueza en el mundo hispanoparlante por eso blogs como el de Enrique Dans son de gran valor para mucha gente que quiere saber lo que pasa sin tener que pasar un curso de informática y otro de inglés.
Recientemente se ha armado un revuelo en el blog de E. Dans debido a que este se hace eco de una noticia acerca de una herramienta de Microsoft que él cataloga como posible backdoor y contra la cual muestra su desagrado. El Sr. Dans usa una palabra que le suena cool para hacer algo que hoy en dia también es cool: atacar a Microsoft. La superficie de ataque a Microsoft es bastante amplia ya que de compañía innovadora a pasado a dinosaurio en un mundo donde todo cambia cada dia y para colmo ha intentado mantenerse en su posición no usando productos de calidad para competir sino la fuerza de su peso. Hasta ahi muy bien. El problema está en que el Sr. Dans hasta el momento de la redacción de su post parece que no tenía la menor idea de lo que era un backdoor por lo que en su post además de su opinión ( tan respetable como la de cualquiera, especialmente dentro de su propio blog ) plantea un hecho que no es cierto.
La reacción no se hizo esperar. Desde los primeros comentarios le intentaron explicar que no, que eso no era una puerta trasera y que el programa en cuestión no necesariamente era un arma del maligno sino una herramienta forense de las tantas que existen y que además no tiene nada que ver con la seguridad de Windows porque tales existen para cualquier sistema operativo.
El hecho de que el creador del sistema operativo sea el que oferte tales herramientas puede ser perfectamente cuestionable y por ahi iba el post, pero se le fue la mano y mezcló las cosas usando temas y términos que desconocía. A esas alturas en otros sitios hubiera aparecido un update aclarando las cosas, no cambiando de opinión sino esclareciendo los terminos, por que ? Pues porque en el mundo hispanoparlante las fuentes de información sobre tecnología no son tan abundantes como en el anglosajón y el Sr. Dans es tenido como "experto" no solo por sus fieles lectores sino también por otras publicaciones. Además, llamarle a una cosa por otro nombre solo porque suena mejor no solo es incorrecto sino que puede caer en el ámbito de la difamación. Que Microsoft ya ha difamado mucho ? Pues si, por eso ( y por muchas otras cosas ) no es de las empresas mas queridas que digamos, pero eso no justifica que nosotros debamos hacer lo mismo.
La respuesta del Sr. Dans ha sido otra entrada donde arremete contra los que le han criticado ( algunos de forma grosera, la verdad sea dicha ) diciendo que en su blog escribe lo que le da la gana y relatando, a modo de constatar autoridad, lo muy leido y referenciado que es. Es decir. Enrique Dans es un formador de opinión y lo sabe. A lo mejor por eso debería tener mas cuidado en investigar lo que va a escribir cuando va a utlizar tecnicismos que no conoce o que no entiende del todo. Pero quién soy yo para decirle a él lo que debe hacer o como debe hacerlo. Por otro lado ni él ni sus fieles son amigos de las cosas técnicas. Hubo un lector que se deshizo en explicaciones para tratar de hacer entender de que iba el tema a otro. Nada. Como con una pared, hasta la guerra en Iraq estuvo por ahi.
Al final lo que sucede es que los lectores de perfil técnico abandonan. Si, porque nosotros no somos tan dados a los debates políticos donde las partes no quieren entender ni saber la verdad, les basta con tener la razón y no la tenemos todos ? Por mi parte me limito a leer los escritos de Enrique en el sitio del Instituto de Empresa que de lo suyo si sabe un montón. A su blog ? Gracias, pero no gracias.
viernes, 2 de mayo de 2008
sábado, 12 de abril de 2008
Pérdidas de memoria en .NET
Una de las mejores cosas de las plataformas "manejadas" como Java y .NET es la posibilidad de no tener que preocuparnos con los detalles del control del uso de la memoria, sin embargo hay situaciones que pueden provocar pérdidas de memoria si no las tenemos en cuenta como intenta explicar Calvin Hsia en Examine .Net Memory Leaks .
Este es un tema que muchas veces confunde, asi que me tomé la libertad de copiar su ejemplo y llevarlo a C#.
Al ejecutar el programa vemos como va consumiendo mas y mas memoria aun cuado en cada iteración estamos forzando la recolección de basura y la instancia que estamos creando no tiene aparentemente ninguna referencia. Aparentemente.
En realidad la referencia si existe porque al "enganchar" un evento a la clase FileSystemWatcher es como si estuviéramos enganchando un evento a un objeto del sistema operativo y mientras tal exista la referencia continuará ahi, no importa que no tengamos en el programa una referencia directa aparente al objeto.
Ahora si quitamos el comentario a la linea que dice
cada llamada al recolector de basura libera la memoria debido a que la llamada a UnSubscribe "desconecta" nuestro objeto del FileSystemWatcher, sin embargo es bueno notar que dos cosas se hacen aqui: una desconectar el evento, la otra liberar el FileSystemWatcher, por que ? El FileSystemWatcher como había dicho representa un objeto del sistema operativo, de hecho en su implementación interna debe tener un handle a un objeto del sistema operativo y no va a ser liberado hasta que se llame a Dispose, podemos hacer la prueba comentando esa linea y ver los resultados.
Como podemos ver aunque .NET maneja la memoria por nosotros, somos responsables cuando desde nuestro programa colocamos referencias a elementos externos, como en este caso objetos del sistema operativo.
Este es un tema que muchas veces confunde, asi que me tomé la libertad de copiar su ejemplo y llevarlo a C#.
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var oldPeak = 0L;
for (int i = 0; i < 100; i++)
{
var oWatcher = new MyWatcher();
// oWatcher.UnSubscribe(); // Quitar comentario para quitar el controlador
oWatcher = null;
GC.Collect(); // Recolectar
GC.WaitForPendingFinalizers(); // Esperar por los finalizadores ( destructures )
GC.Collect(); // Recolectar los que tienen destructores
var newPeack = System.Diagnostics.Process.GetCurrentProcess().PeakWorkingSet64;
if (i > 0)
Console.WriteLine("Todo liberado? {0} WorkingSet ={1} Pico ={2} Delta ={3}",
i, System.Diagnostics.Process.GetCurrentProcess().WorkingSet64,
newPeack, (newPeack - oldPeak));
oldPeak = newPeack;
}
GC.Collect(); // Recolectar
GC.WaitForPendingFinalizers(); // Esperar por los finalizadores ( destructures )
GC.Collect(); // Recolectar los que tienen destructores
Console.ReadKey();
}
}
class MyWatcher
{
string[] _myLargeMemoryEater = new string[100000];
System.IO.FileSystemWatcher _fsw;
public MyWatcher()
{
_fsw = new System.IO.FileSystemWatcher();
_fsw.Path = "c:\\";
_fsw.Filter = "*.*";
_fsw.Created += _fsw_Created;
_fsw.EnableRaisingEvents = true;
}
public void UnSubscribe()
{
_fsw.Created -= _fsw_Created;
_fsw.Dispose();
}
void _fsw_Created(object sender, System.IO.FileSystemEventArgs e)
{
Console.WriteLine(e.FullPath);
}
~MyWatcher()
{
Console.WriteLine("Terminado en hilo "+System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}
Al ejecutar el programa vemos como va consumiendo mas y mas memoria aun cuado en cada iteración estamos forzando la recolección de basura y la instancia que estamos creando no tiene aparentemente ninguna referencia. Aparentemente.
En realidad la referencia si existe porque al "enganchar" un evento a la clase FileSystemWatcher es como si estuviéramos enganchando un evento a un objeto del sistema operativo y mientras tal exista la referencia continuará ahi, no importa que no tengamos en el programa una referencia directa aparente al objeto.
Ahora si quitamos el comentario a la linea que dice
// oWatcher.UnSubscribe();
cada llamada al recolector de basura libera la memoria debido a que la llamada a UnSubscribe "desconecta" nuestro objeto del FileSystemWatcher, sin embargo es bueno notar que dos cosas se hacen aqui: una desconectar el evento, la otra liberar el FileSystemWatcher, por que ? El FileSystemWatcher como había dicho representa un objeto del sistema operativo, de hecho en su implementación interna debe tener un handle a un objeto del sistema operativo y no va a ser liberado hasta que se llame a Dispose, podemos hacer la prueba comentando esa linea y ver los resultados.
Como podemos ver aunque .NET maneja la memoria por nosotros, somos responsables cuando desde nuestro programa colocamos referencias a elementos externos, como en este caso objetos del sistema operativo.
miércoles, 9 de abril de 2008
Donde está Oracle Home ?
El Oracle Home es, entre otras cosas, la forma de localizar los productos de Oracle en una máquina. Pueden haber varias pero siempre hay una por defecto que es la utilizada por la mayoría de las aplicaciones que dependen de un cliente de oracle para funcionar.
En un mundo ideal el cliente de Oracle es instalado por un administrador y las aplicaciones solo tienen que usar el que esté ahi y no preocuparse mucho mas. En el mundo real el cliente es instaldo muchas veces por cualquiera y en muchas ocaciones es necesario saber donde está. Si como parte de la configuración de tu aplicación necesitas decir a que instancia/servicio de Oracle te conectas es posible que necesites saber cuales son las que están disponibles en la máquina y como esa información está en el archivo tnsnames.ora , tienes que saber en que directorio está instalado oracle home por defecto.
Siendo una cosa tan importante es curioso que halla tanta confusión sobre esto especialmente en plataforma Windows. Eso se debe en parte a que la documentación de Oracle al respecto no es muy abundante.
Lo que yo he implementado y hasta el momento me ha funcionado bien ( creo que lo vi en alguna parte en la documentación de Oracle ) es que hay un archivo llamado oracle.key en el directorio bin de la instalación que dice en que lugar en el registro de Windows está la información del Oracle Home. Por lo tanto para encontrar el ORACLE_HOME:
1 .- Recorrer los directorios que están en el camino ( variable de ambiente PATH, separados por punto y coma ).
2 .- Detenerse en el primero donde se encuentre el archivo oracle.key.
3 .- En ese archivo está el lugar en el registro donde está la información del ORACLE_HOME ( por ejemplo SOFTWARE\ORACLE\KEY_OraDb10g_home1 ).
Y ya tenemos la ubicación del Oracle Home en el registro, de ahi ganamos otros datos interesantes como directorios , valores de configuración , ubicación de otros productos, etc.
En un mundo ideal el cliente de Oracle es instalado por un administrador y las aplicaciones solo tienen que usar el que esté ahi y no preocuparse mucho mas. En el mundo real el cliente es instaldo muchas veces por cualquiera y en muchas ocaciones es necesario saber donde está. Si como parte de la configuración de tu aplicación necesitas decir a que instancia/servicio de Oracle te conectas es posible que necesites saber cuales son las que están disponibles en la máquina y como esa información está en el archivo tnsnames.ora , tienes que saber en que directorio está instalado oracle home por defecto.
Siendo una cosa tan importante es curioso que halla tanta confusión sobre esto especialmente en plataforma Windows. Eso se debe en parte a que la documentación de Oracle al respecto no es muy abundante.
Lo que yo he implementado y hasta el momento me ha funcionado bien ( creo que lo vi en alguna parte en la documentación de Oracle ) es que hay un archivo llamado oracle.key en el directorio bin de la instalación que dice en que lugar en el registro de Windows está la información del Oracle Home. Por lo tanto para encontrar el ORACLE_HOME:
1 .- Recorrer los directorios que están en el camino ( variable de ambiente PATH, separados por punto y coma ).
2 .- Detenerse en el primero donde se encuentre el archivo oracle.key.
3 .- En ese archivo está el lugar en el registro donde está la información del ORACLE_HOME ( por ejemplo SOFTWARE\ORACLE\KEY_OraDb10g_home1 ).
Y ya tenemos la ubicación del Oracle Home en el registro, de ahi ganamos otros datos interesantes como directorios , valores de configuración , ubicación de otros productos, etc.
domingo, 2 de marzo de 2008
Herencia múltiple en C# ( resuelta )
Bueno, en realidad no, no hay forma de resolver el tema de la herencia múltiple en C# sin modificar el lenguage, pero siempre podemos acceder a soluciones alternativas mas o menos funcionales para lograr los mismos resultados que queríamos obtener con la herencia múltiple.
Estas soluciones alternativas dependen en general de los medios que poseemos y los resultados esperados. En muchos casos el problema es que tenemos una clase utilitaria que espera objetos de un tipo determinado ( no una interface ), para que nuestro objeto pueda ser utilizado en ese contexto heredamos de aquel y listo:
Hasta aqui todo bien. Pero que pasa si nuestro tipo ya heredaba de otro ? Si hubiera herencia múltiple heredábamos de Usable y de nuestro tipo base pero como no hay siempre podemos instanciar un objeto de tipo Usable y publicarlo para luego usarlo:
Evidentemente este ejemplo tiene una falla fundamental: esto no tiene nada que ver con la herencia. Si queríamos usar la herencia es porque probablemente hay algo dentro del tipo Usable que queríamos reescribir asi que simplemente instanciarlo dentro del nuestro no resuelve ningún problema. Al final lo que queremos es redefinir el comportamiento del tipo Usable y además queremos en esta redefinición haga uso de las interioridades de nuestro tipo. La solución es crear una clase interna que herede del tipo Usable y que redefina lo que sea necesario, además sería necesario siempre pasarle una instancia de nuestro objeto para poder tener acceso a sus interioridades. Algo asi:
Esto resuelve el problema en parte, aunque nos deja todavía con la necesidad de instanciar el objeto, cosa que además tiene que hacerse en el constructor ya que no nos permiten instanciarlo en la declaración pasándole además this como parámetro.
Lo mejor evidentemente sería tener herencia múltiple nativa en el lenguage pero como no hay podemos acudir a este tipo de mecanismos para simularla cuando la necesidad se presenta.
Estas soluciones alternativas dependen en general de los medios que poseemos y los resultados esperados. En muchos casos el problema es que tenemos una clase utilitaria que espera objetos de un tipo determinado ( no una interface ), para que nuestro objeto pueda ser utilizado en ese contexto heredamos de aquel y listo:
// Este es el tipo que se usa en la clase utilitaria
public class Usable
{
public virtual void HacerAlgo()
{
Console.WriteLine("Hice algo");
}
}
// Esta es la clase utilitaria
public class Utilitaria
{
public void Usa(Usable u)
{
u.HacerAlgo();
}
}
// Y este es nuestro tipo
class NuestroTipo : Usable
{
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa");
}
}
Hasta aqui todo bien. Pero que pasa si nuestro tipo ya heredaba de otro ? Si hubiera herencia múltiple heredábamos de Usable y de nuestro tipo base pero como no hay siempre podemos instanciar un objeto de tipo Usable y publicarlo para luego usarlo:
class NuestroTipo : NuestroTipoBase
{
private Usable m_usa = new Usable();
public Usable Usar { get { return m_usa; } }
}
...
Utilitaria util = new Utilitaria();
NuestroTipo tipo = new NuestroTipo();
util.Usa(tipo.Usar);
Evidentemente este ejemplo tiene una falla fundamental: esto no tiene nada que ver con la herencia. Si queríamos usar la herencia es porque probablemente hay algo dentro del tipo Usable que queríamos reescribir asi que simplemente instanciarlo dentro del nuestro no resuelve ningún problema. Al final lo que queremos es redefinir el comportamiento del tipo Usable y además queremos en esta redefinición haga uso de las interioridades de nuestro tipo. La solución es crear una clase interna que herede del tipo Usable y que redefina lo que sea necesario, además sería necesario siempre pasarle una instancia de nuestro objeto para poder tener acceso a sus interioridades. Algo asi:
// Este es nuestro tipo que hereda de otro
public class NuestroTipo : NuestroTipoBase
{
// Este es nuestro usable interno
private class NuestroUsable : Usable
{
// Evidentemente necesitamos acceder a las
// interioridades del objeto master
NuestroTipo m_master;
public NuestroUsable(NuestroTipo master)
{
m_master = master;
}
public override void HacerAlgo()
{
Console.WriteLine("Hacer otra cosa ( internamente )");
}
}
private readonly NuestroUsable m_usa;
public Usable Usar { get { return m_usa; } }
public NuestroTipo()
{
m_usa = new NuestroUsable(this);
}
}
Esto resuelve el problema en parte, aunque nos deja todavía con la necesidad de instanciar el objeto, cosa que además tiene que hacerse en el constructor ya que no nos permiten instanciarlo en la declaración pasándole además this como parámetro.
Lo mejor evidentemente sería tener herencia múltiple nativa en el lenguage pero como no hay podemos acudir a este tipo de mecanismos para simularla cuando la necesidad se presenta.
viernes, 29 de febrero de 2008
Herencia múltiple
C# no tiene herencia múltiple, Java tampoco, Delphi tampoco. De los lenguages mas usados solo C++ tiene herencia múltiple y en la mayoría de los programas que he visto no es usada. Sin embargo es una de las características que mas extraño en mi viaje por .NET desde C++ porque ayuda a resolver muchos problemas. Me he encontrado conque quiero que una clase sea un componente por el uso que le voy a dar ( hereda de System.ComponentModel.Component ) pero además quiero que se beneficie de un mecanismo de persistencia que he creado. Al final terminas por hacer magia para que el mecanimo no heredado sea capaz de adivinar de todo ( reflection mediante ) o te creas una interface.
El método socorrido de la interface tiene la desventaja de que tienes que implementar los métodos de esta. En muchos casos instancias dentro de la clase un objeto que va a hacer el trabajo y los métodos de la interface solo llaman a los métodos de ese objeto pero esa solución es extremadamente trabajosa si la interface tiene varios métodos.
Sería interesante que se pudiera "delegar" en un objeto. Me gustaría poder hacer automáticamente lo que tanto he tenido que hacer a mano. Decirle al compilador que los métodos de la interface son implementados por un objeto de la clase. Eso en parte se puede lograr en .NET 3 con los métodos de extensión, pero solo en parte, se puede tener la funcionalidad agregada a la clase pero no se pueden aprovechar las ventajas de la herencia. Una solución podría ser en la definición de la clase decir como hasta ahora:
class Ejemplo : IEjemploInterface
pero cuando se declare el objeto interno que implementa los métodos de la interface decir algo como:
implements IEjemploInterface ObjetoInterface obj;
para decir que los llamados a los métodos de la interface se "delegan" en la instancia de ese objeto. Eso sería muy útil ... y me resolvería muchos problemas.
El método socorrido de la interface tiene la desventaja de que tienes que implementar los métodos de esta. En muchos casos instancias dentro de la clase un objeto que va a hacer el trabajo y los métodos de la interface solo llaman a los métodos de ese objeto pero esa solución es extremadamente trabajosa si la interface tiene varios métodos.
Sería interesante que se pudiera "delegar" en un objeto. Me gustaría poder hacer automáticamente lo que tanto he tenido que hacer a mano. Decirle al compilador que los métodos de la interface son implementados por un objeto de la clase. Eso en parte se puede lograr en .NET 3 con los métodos de extensión, pero solo en parte, se puede tener la funcionalidad agregada a la clase pero no se pueden aprovechar las ventajas de la herencia. Una solución podría ser en la definición de la clase decir como hasta ahora:
class Ejemplo : IEjemploInterface
pero cuando se declare el objeto interno que implementa los métodos de la interface decir algo como:
implements IEjemploInterface ObjetoInterface obj;
para decir que los llamados a los métodos de la interface se "delegan" en la instancia de ese objeto. Eso sería muy útil ... y me resolvería muchos problemas.
martes, 29 de enero de 2008
Cuando se habla de .NET 3 lo primero que salta a la mente es LINQ. Todo el mundo habla de LINQ como una maravilla de la técnica, y en realidad es bastante interesante.
Imaginemos que tenemos la clase Persona:
class Persona
{
string m_nombre;
string m_apellido;
public Persona(string nombre, string apellido)
{
m_nombre = nombre;
m_apellido = apellido;
}
public string Nombre { get { return m_nombre; } }
public string Apellido { get { return m_apellido; } }
public override string ToString()
{
return Nombre + " " + Apellido;
}
}
y tenemos una colección de personas:
List< Persona > personas = new List< Persona >();
personas.Add(new Persona("Rodrigo", "Rojas"));
personas.Add(new Persona("Armando","Fuentes"));
personas.Add(new Persona("Lucia", "Wong"));
personas.Add(new Persona("Maritza","Rojas"));
usando LINQ podríamos buscar a todos los "Rojas" asi:
var a = from p in personas
where p.Apellido == "Rojas"
select p;
foreach (Persona per in a)
{
Console.WriteLine("{0}", per);
}
Ya se que también podría preguntar en el ciclo, pero es evidente que se ve cool por un lado, por otro lado el ejemplo es idiotamente simplista pero las consultas que se construyen con LINQ pueden ser bastante mas complejas, sin contar con el hecho de que separar el ciclo de la selección tiene la ventaja de poder usar el mismo ciclo en selecciones diferentes, algo que ya se hace en .NET 2 con el yield return pero que aqui se lleva mas lejos con este mecanismo que permite estandarizar una buena parte de las búsquedas sobre colecciones, algo tan común en cualquier programa.
Con todo esto del LINQ no se notan los mecanismos verdaderamente poderosos que permiten que exista, estos son los métodos de extensión (extension methods) y el patrón consulta (query expression pattern).
Los métodos de extensión no son mas que una nueva forma de llamar a un método estático que permite que la invocación sea , quizas , mas clara. Por ejemplo: Vamos a suponer que queremos en el código anterior obtener el nombre de una manera mas ... formal, en vez de nombre+apellido, queremos apellido+","+nombre, pero supongamos que esa clase no fue escrita por nosotros y no podemos ir para atrás y modificarla por los motivos que fueran. Lo mas probable es que creemos un método estático en alguna clase, que si además solo hace eso también será estática, y que nos haga el trabajo, algo asi:
static class Nombrar
{
public static string NombreFormal(Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}
el ciclo anterior quedaría asi:
foreach (Persona per in a)
{
Console.WriteLine("{0}", Nombrar.NombreFormal(per));
}
Ahora convertimos NombreFormal en método de extensión con un ligero cambio:
static class Nombrar
{
public static string NombreFormal(this Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}
y el ciclo se puede escribir asi:
foreach (Persona per in a)
{
Console.WriteLine("{0}", per.NombreFormal());
}
Es todo una cuestión de imagen, pero queda sin duda mucho mejor.
Y ahora entra el patrón consulta. Este dice que si una clase implementa ciertos métodos entonces puede usarse en una expresión de consulta, como la que vimos al inicio, estos métodos son enrte otros Select, Where, Join, OrderBy , entre otros. Y que es LINQ ? Pues LINQ no es mas que la implementación de esos métodos para cualquier tipo que implemente la interface System.Collections.Generic.IEnumerable< t > por eso es que se puede usar con cualquier colección, hasta con un arreglo. Lo poderoso de esto es que uno puede hacer su propio LINQ, es decir, implementar estos métodos para hacer que nuestras clases se comporten como orígenes de datos "consultables".
Imaginemos que tenemos la clase Persona:
class Persona
{
string m_nombre;
string m_apellido;
public Persona(string nombre, string apellido)
{
m_nombre = nombre;
m_apellido = apellido;
}
public string Nombre { get { return m_nombre; } }
public string Apellido { get { return m_apellido; } }
public override string ToString()
{
return Nombre + " " + Apellido;
}
}
y tenemos una colección de personas:
List< Persona > personas = new List< Persona >();
personas.Add(new Persona("Rodrigo", "Rojas"));
personas.Add(new Persona("Armando","Fuentes"));
personas.Add(new Persona("Lucia", "Wong"));
personas.Add(new Persona("Maritza","Rojas"));
usando LINQ podríamos buscar a todos los "Rojas" asi:
var a = from p in personas
where p.Apellido == "Rojas"
select p;
foreach (Persona per in a)
{
Console.WriteLine("{0}", per);
}
Ya se que también podría preguntar en el ciclo, pero es evidente que se ve cool por un lado, por otro lado el ejemplo es idiotamente simplista pero las consultas que se construyen con LINQ pueden ser bastante mas complejas, sin contar con el hecho de que separar el ciclo de la selección tiene la ventaja de poder usar el mismo ciclo en selecciones diferentes, algo que ya se hace en .NET 2 con el yield return pero que aqui se lleva mas lejos con este mecanismo que permite estandarizar una buena parte de las búsquedas sobre colecciones, algo tan común en cualquier programa.
Con todo esto del LINQ no se notan los mecanismos verdaderamente poderosos que permiten que exista, estos son los métodos de extensión (extension methods) y el patrón consulta (query expression pattern).
Los métodos de extensión no son mas que una nueva forma de llamar a un método estático que permite que la invocación sea , quizas , mas clara. Por ejemplo: Vamos a suponer que queremos en el código anterior obtener el nombre de una manera mas ... formal, en vez de nombre+apellido, queremos apellido+","+nombre, pero supongamos que esa clase no fue escrita por nosotros y no podemos ir para atrás y modificarla por los motivos que fueran. Lo mas probable es que creemos un método estático en alguna clase, que si además solo hace eso también será estática, y que nos haga el trabajo, algo asi:
static class Nombrar
{
public static string NombreFormal(Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}
el ciclo anterior quedaría asi:
foreach (Persona per in a)
{
Console.WriteLine("{0}", Nombrar.NombreFormal(per));
}
Ahora convertimos NombreFormal en método de extensión con un ligero cambio:
static class Nombrar
{
public static string NombreFormal(this Persona per)
{
return per.Apellido + ", " + per.Nombre;
}
}
y el ciclo se puede escribir asi:
foreach (Persona per in a)
{
Console.WriteLine("{0}", per.NombreFormal());
}
Es todo una cuestión de imagen, pero queda sin duda mucho mejor.
Y ahora entra el patrón consulta. Este dice que si una clase implementa ciertos métodos entonces puede usarse en una expresión de consulta, como la que vimos al inicio, estos métodos son enrte otros Select, Where, Join, OrderBy , entre otros. Y que es LINQ ? Pues LINQ no es mas que la implementación de esos métodos para cualquier tipo que implemente la interface System.Collections.Generic.IEnumerable< t > por eso es que se puede usar con cualquier colección, hasta con un arreglo. Lo poderoso de esto es que uno puede hacer su propio LINQ, es decir, implementar estos métodos para hacer que nuestras clases se comporten como orígenes de datos "consultables".
sábado, 19 de enero de 2008
La paradoja de Galiano
Hay gente que simplemente sabe como decir las cosas y hay dias en que lo que esa gente dice nos obliga a pensar un poco en lo injusta que es la historia y nos hace sentir un poco mal, por nuestro bien. Eso me ha pasado con La paradoja andante de Eduardo Galiano.
Suscribirse a:
Entradas (Atom)