martes, 22 de septiembre de 2009

Oracle SQLDeveloper en Windows Vista

En Internet una de las cosas que mas me molestan es que buscas algo y aparecen cientos de veces la misma información copiada hasta la saciedad por todo el mundo. Sin embargo ahora yo voy a formar parte de esa cadena de repetidores porque he encontrado la forma de resolver mi problema y quiero compartirlo.

Hace tiempo que he decidido utilizar el SQLDeveloper, una herramienta de Oracle para acceder a bases de datos. Esta herramienta es gratis y como he decidido reducir a cero el uso de software pirata, el Toad está fuera de mi alcance.

El SQLDeveloper no es el mejor de su clase pero consigue satisfacer mis necesidades de acceso a bases Oracle. La instalación es rasonablemente facil: bajarlo y correrlo. Desafortunadamente para los que usamos Windows Vista, muchas veces la visualización se hace imposible, especialmente cuando haces scroll, ya que todo queda como montado y descontrolado. Viví con ese problema durante bastante tiempo y en varios momentos me cuestioné seguirlo usando ( hasta he pensado en hacer uno, muy simple, en C# ) hasta que hace poco encontré unas soluciones aqui y aqui que me han sido muy utiles.

La solución se resume a:

1.- Obviamente, tener instalado el último SQLDeveloper.

2.- Bajar la última versión del java jdk. Si no se es programador de java lo mejor es bajar el que menos cosas trae, pero cualquiera sirve.

3.- En el directorio donde está instalado el SQLDeveloper hay un directorio llamado ide que dentro tiene uno llamado bin ( en mi caso c:\sqldeveloper\ide\bin\ ), en ese directorio hay dos archivos que hay que editar: el ide.conf y el jdk.conf.

3.1- En el ide.conf hay que agregar las lineas:

AddVMOption -Xmx256M
AddVMOption -Dsun.awt.keepWorkingSetOnMinimize=true

3.2- En el jdk.conf hay que decir donde está el jdk ( donde lo instalamos ), en mi caso en C:\Program Files\Java\jdk1.6.0_16, por lo que tengo que agregar en el jdk.conf la linea:

SetJavaHome C:\Program Files\Java\jdk1.6.0_16

Y eso fue todo, a partir de ahi ha funcionado perfectamente.

Además de eso en la configuración del SQLDeveloper, en el menú Tools > Preferences, la sección Enviroment, hay una opción que se llama "Look and Feel", normalmente ahi dice Oracle pero se puede pasar a Windows con lo que el visual queda mas Windows-like y en los comentarios de uno de los post que mencioné anteriormente hay quien dice que hasta mejora en eficiencia. La verdad es que a mi aunque me gustó, la opción de propiedades de las conexiones dejó de funcionarme, y lo regresé a la opción Oracle.

Remedio santo.

viernes, 19 de diciembre de 2008

Esperas paralelas

La programación concurrente suele tener sus particularidades que hace que mucha gente la evite siempre que puede lo que es una lástima porque muchas cosas se pueden hacer mejor cuando se aprovecha la posibilidad de que un proceso ejecute paralelo al actual. Es también una lástima porque luego aparecen muchas apliaciones que a pesar de ser muy útiles hay momentos en que quedan paradas si hacer nada en lo que se ejecuta algún proceso que demora mucho y que tampoco podemos interrumpir.

Uno de los errores de implementación que mas me encuentro cuando alguien nuevo en la materia intenta programar algo en paralelo son las esperas. Necesitamos esperar generalmente porque estamos a espera de algo que no ha sucedido, como que esté disponible algún recurso o que algún proceso termine. Además del hecho mismo de esperar, el programador consciente se protege de la eventualidad de que aquello por lo que estamos esperando nunca suceda.

Aunque en .NET hay varias formas de implementar las esperas, aparentemente lo primero que se le ocurre a mucha gente es esperar durante un tiempo, probablemente porque es lo mas fácil y porque existen equivalentes cuando no se programa en paralelo. Generalmente esta espera viene en dos sabores:

El del programador inconsciente:

using System;

namespace paralelos
{
class Program
{
static volatile bool EsperaPorMi = false;

static void Main(string[] args)
{
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

System.Threading.Thread.Sleep(6000); // Esta es la espera

// La finalización del proceo paralelo será marcada por el valor de la
// variable EsperaPorMi
if (EsperaPorMi)
Console.WriteLine("El proceso paralelo ha terminado");
else
Console.WriteLine("El proceso paralelo no ha terminado");
Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EsperaPorMi = true;
}
}
}


Aqui el programador inconsciente supone que sabe cuanto debe demorar la operación paralela y por lo tanto lo único que tiene que hacer es esperar un poco mas y asi garantiza que cuando deja de esperar la operación ya terminó.

Desafortunadamente esta solución tiene varios fallos obvios.
Para empezar, determinar con exactitud el tiempo que demora una sección de código de un programa es poco menos que imposible en el 95% de los casos en un sistema convencional. Estos tiempos dependen mucho de la configuración sobre la que ejecuta el programa ( procesador y sus nucleos, otros programas instalados y corriendo, procesos del sistema operativo, eventos que ocurran como la abertura de una torre de cd o un acceso remoto al pc, y un largo etcétera ). Si además la operación por la que estamos esperando tiene que acceder a recursos fuera de la gestión pura del programa ( archivos, bases de datos, etc. ) mas impredecible se torna. Finalmente y en caso de que la operación sea muy demorada ( y por tanto también la espera ) mas nada es hecho durante ese tiempo y por tanto se pierde en parte el beneficio de la ejecución paralela.

El programador consciente toma en cuenta los problemas anteriores y hace esto:

using System;

namespace paralelos
{
class Program
{
static volatile bool EsperaPorMi = false;

static void Main(string[] args)
{
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

Console.WriteLine("Presione cualquier tecla para interrumpir");
// Ahora viene la espera
while (!EsperaPorMi)
{
System.Threading.Thread.Sleep(500); // Dormimos poco

if (Console.KeyAvailable) // El usuario se cansó de esperar ?
{
Console.Read();
break;
}
}

// La finalización del proceo paralelo será marcada por el valor de la
// variable EsperaPorMi
if (EsperaPorMi)
Console.WriteLine("El proceso paralelo ha terminado");
else
Console.WriteLine("El proceso paralelo no ha terminado");
Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EsperaPorMi = true;
}
}
}

En este caso el programador no pretende saber cuanto demora el proceso paralelo asi que en vez de esperar dormido durante tiempo suficiente lo que hace es verificar continuamente el estado de la variable de terminación y además verifica si el usuario pulsó una tecla indicando que ya no quiere esperar mas. Sin embargo, consciente como es, entre consulta y consulta duerme un poco, por que ?

El encargado de ejecutar las instrucciones de nuestro programa ( o mas estrictamente hablando , en lo que ellas se convierten ) es el procesador de la computadora. En un pc suelen haber en la mayoría de los casos de 1 a 4 procesadores o nucleos de procesadores con lo cual en cada momento solo pueden estarse ejecutando tantos procesos como procesadores x nucleos tengamos. Si nos damos un paseo por el task manager ( Ctrl+Shift+Esc ) y vamos a la pestaña de procesos vamos a ver que potencialmente hay muchos procesos que pueden necesitar de ejecutar y cada uno de ellos puede estar formado por muchos hilos cada uno de los cuales también necesita correr. El sistema operativo "planifica" al procesador para que le dedique un intervalo de tiempo a cada proceso/hilo y pasado ese tiempo pase a otro; eso ocurre muchas veces en cada milisegundo con lo cual nos da la idea de que se pueden ejecutar en paralelo mas procesos/hilos de los que serían posibles a juzgar por la cantidad de procesadores y nucleos que tengamos. Por lo tanto podemos concluir que el tiempo de procesador es un recurso bastante escaso, por eso el sistema no planifica al procesador ciegamente y una de las optimizaciones básicas que hace es no darle tiempo de procesador a quien no lo necesita, por lo tanto si un hilo está "dormido" por cualquier motivo el sistema no le dará tiempo de procesador con lo cual da la posibilidad de ejecutar otros que si lo necesitan. En el ejemplo anterior lo único que se hace entre espera y espera es ver si se ha precionado una tecla por lo tanto nos podemos tomar la libertad de dormir un poco ( 1/2 segundo ) antes de verificar el teclado y como el tiempo es tan corto el usuario ni se da cuenta. Si no lo hicieramos asi veríamos como aumenta el consumo de procesador con lo cual estamos ocupando sin motivos el tiempo que podría dedicarse a otro programa relentizando el performance general de la máquina.

Esta segunda solución es mejor que la primera y mas lógica, sin embargo hay casos en que ese medio segundo puede resultar mucho tiempo, especialmente si el programa ejecuta en un computador muy rápido. En esos casos no tiene sentido esperar mas de lo necesario y entonces lo que necesitamos es quedarnos dormidos y que sea el proceso paralelo el que nos despierte:

using System;

namespace paralelos
{
class Program
{
static System.Threading.ManualResetEvent EventoManual =
new System.Threading.ManualResetEvent(false);

static void Main(string[] args)
{
EventoManual.Reset(); // Nos aseguramos que no está señalizado
// Mandamos a ejecutar un proceso paralelo
System.Threading.ThreadPool.QueueUserWorkItem(Paralelo);

// Supuestamente aqui hacemos otras cosas antes de decidir esperar
// ...

Console.WriteLine("Presione cualquier tecla para interrumpir");
// Ahora viene la espera
while (true)
{
if (EventoManual.WaitOne(500)) // Dormimos solo lo necesario
{
// Si fue señalizado salimos con éxito
Console.WriteLine("El proceso paralelo ha terminado");
break;
}

if (Console.KeyAvailable) // El usuario se cansó de esperar ?
{
// No ha sido señalizado y el usuario ya no quiere esperar mas
// salimos sin éxito
Console.Read();
Console.WriteLine("El proceso paralelo no ha terminado");
break;
}
}

Console.WriteLine("\nPresione cualquier tecla para salir");
Console.ReadKey();
}

// Este es el proceso paralelo
static void Paralelo(object estado)
{
// Esto es para simular una operación que demora cinco segundos
System.Threading.Thread.Sleep(5000);
EventoManual.Set();
}
}
}


Aqui hemos quitado la variable de espera y la hemos sustituido por un evento manual. El evento tiene dos estados: señalizado y no señalizado. En su estado señalizado si llamamos al método WaitOne este retorna inmediatamente mientras que en el estado no señalizado el mismo llamado provoca que la ejecución quede bloqueada hasta que alguien ( otro proceso/hilo ) señalice al evento y desbloquee nuestra ejecución. Además el método WaitOne tiene la opción de pasarle el tiempo que queremos quedar bloqueados y si este pasa el retorna el valor false diciendo que nos desbloqueamos porque pasó el tiempo que le dijimos mientras que si fuimos desbloqueados por otro proceso retorna true.

En .NET tenemos muchas herramientas para tratar con los problemas que la concurrencia plantea, solo que es necesario saber usar los adecuados en casa caso.

viernes, 2 de mayo de 2008

Mejor me voy

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.

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#.


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.

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:


// 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.