viernes, 10 de septiembre de 2010

OBTENIENDO UNA REFERENCIA AL OBJETO QUE INVOCA A UN METODO EN C#

Ok, luego de tomarme un mes de asueto de cualquier actividad de blogueo, supongo que es tiempo de ponerme al día y publicar de nuevo. Y creo que no hay mejor forma de hacerlo que escribir un artículo explicando una forma de obtener una referencia a un objeto que llama a un método específico.

Durante los últimos dos años he estado trabajando duro en un API de reemplazo al Content Manager del Framework de XNA para usarla en mi propio motor de videojuegos “que nunca finaliza” (voy a escribir sobre esto más adelante). En etapas tempranas de su desarrollo encontré una forma de solucionar esta traba referencial que ha estado creando dolores de cabeza a muchos desarrolladores, globalmente (siendo yo, uno de ellos, por mucho tiempo).

Aunque debo admitir que en algún punto del ciclo de desarrollo de mi API decidí abandonar esta solución “parche”, luego de leer uno de los artículos mencionados por Shawn aquí (adivinen cuál), entiendo que es una gran oportunidad para hablar de ello.

Un poco de historia … previo a encontrar esta solución lo intenté todo: desde probar usar, sin suerte, la información contenida en instancias del StackFrame e incluso delegados hasta finalmente “desistir” y utilizar un enfoque ingenuo como ser el pasar al invocador como parámetro en la firma de la operación. Pero, luego vino el Framework .NET 3 y los métodos de extensión al rescate!

Por favor coincidan conmigo que el ejemplo presentado en este artículo es uno simple para mostrar su uso, pero créanme cuando digo que puede extenderse a escenarios complejos.

Dicho eso … si desean saber cuál objeto está invocando a un cierto método de una clase como (que puede esta sellada o nó):

  public sealed class MyCalledClass
  {
    internal bool JustReturnTrue(int myParameter)
    {
      return true;
    }
  }

Siendo el invocador, digamos, una especificación de la siguiente clase:

  public abstract class MyCallingClass
  {
    public void Print()
    {
      Console.WriteLine("I am the object which is just about to call
         the monitored operation named 'JustReturnTrue' …"
);
    }
  }

Entonces todo lo que tienen que hacer es implementar el método de extensión que sigue para los tipos que deseen (en mi ejemplo, “MyCallingClass”):

  public static class MyExtensionMethod
  {
    public static bool ExecuteOperation<T>
      (this T mycallingClass, int myParameter)
      where T: MyCallingClass
    {
      mycallingClass.Print(); // Cámbienlo con sus propias cosas.
 
      return new MyCalledClass().JustReturnTrue(myParameter);
    }
  }

El truco aquí es diseñar y separar ensamblados y espacios de nombre de forma tal que todas las instancias de “MyCallingClass” no puedan ejecutar directamente la operación “JustReturnTrue” (dejo esa tarea como un ejercicio para el lector).

Pero hay un tema a seguir de cerca, sin embargo. Al hacer esto están de hecho agregando una llamada más (cosa que ya saben), lo cual no es en general un problema en Windows, pero para la XBox 360 y todos los dispositivos que utilicen el Compact Framework, podría resultar caro si se utiliza muchas veces en tarea o bucles intensivos o pesados.

Pero si Uds. realmente lo necesitan cuando la velocidad no es un inconveniente o para operaciones donde, por ejemplo, precisen establecer dueños de algo “automágicamente” tras bambalinas y luego evaluar si un dueño llama a una operación restringida antes de ejecutarla, entonces ahí lo tienen!

Sólo úsenlo sabiamente …
~Pete

> Vínculo a la versión en inglés.

No hay comentarios: