Básicamente, una variable "dinámica" le dirá al compilador que ignore el chequeo de su tipo de dato, por lo que cualquier llamada a una operación no válida no será detectada hasta el momento de ejecución. Por ejemplo, el siguiente código compilará perfectamente a pesar que la clase "Int32" no cuenta con un método llamado "Whatever" (por supuesto que una vez ejecutada, la aplicación mostrará el error):
dynamic i = new Int32(); i.Whatever();Una de las situaciones donde "dynamic" resulta muy útil es en el área de interoperabilidad entre clases y/o componentes. Para poner un ejemplo bien simple, partamos de la siguiente clase que utilizaremos a lo largo del artículo:
public class Test { public int Sum(int a, int b) { return a + b; } public int Value { get; set; } }Ahora digamos que en nuestro código no podemos acceder directamente a los métodos y propiedades de la clase "Test", y que solo podemos hacerlo a través de reflection. Suponiendo que queramos imprimir la suma de dos valores cualesquiera usando el método "Sum", el código quedaría más o menos de la siguiente forma:
object test = new Test(); Type type = typeof(Test); var result = type.InvokeMember("Sum", BindingFlags.InvokeMethod, null, test, new object[] { 1, 2 }); Console.WriteLine(result);No solo es bien complicado el código anterior, sino que además invoca el método utilizando una cadena de caracteres mágica (magic string - "Sum"). Esto puede provocar que si el método cambia de nombre, tengamos un problema en caso que olvidemos cambiar también la invocación. Ni corto ni perezoso, C# 4.0 viene en auxilio resolviendo el mismo ejemplo de una forma mucho más elegante:
dynamic test = new Test(); var result = test.Sum(1, 2); Console.WriteLine(result);A pesar que los resultados son los mismos, usando la palabra reservada "dynamic" logramos que el código sea mucho más claro y evitamos usar cadenas mágicas. En un ejemplo pequeño como el anterior las ventajas no resultan tan evidentes, pero en un código mucho más grande y complicado los beneficios se harán notar desde el primer momento.
Pero hay algo más...
La primera vez que vi variables dinámicas en uso, no pude evitar preguntarme cuán eficiente sería esto comparado con el uso de reflection. La mejor forma de salir de dudas es hacer un pequeño experimento y comparar los tres casos posibles (acceder directamente a una propiedad, acceder a través de reflection, y acceder dinámicamente):
var test = new Test(); DateTime startTime = DateTime.Now; for (int i = 0; i < 1000000; i++) { test.Value = 0; } Console.WriteLine(DateTime.Now.Subtract(startTime).ToString());El ejemplo anterior no hace más que acceder directamente a la propiedad "Value" de la clase "Test" para asignarle el valor 0. Esta operación la realiza un millón de veces para así tener algo que podamos medir. Obviamente, accediendo de forma directa a la propiedad es la vía más eficiente, y si la incluyo aquí es solo para tener un valor inicial de comparación. Luego de ejecutar el ejemplo, el tiempo impreso en mi procesador fue "00:00:00.0090005". Veamos ahora qué sucede si usamos reflection para acceder a la propiedad "Value":
var test = new Test(); var property = typeof(Test).GetProperty("Value"); DateTime startTime = DateTime.Now; for (int i = 0; i < 1000000; i++) { property.SetValue(test, 0, null); } Console.WriteLine(DateTime.Now.Subtract(startTime).ToString());El tiempo impreso en pantalla no me sorprendió ya que fué de "00:00:01.4470816", o sea, casi un segundo y medio, cuando accediendo directamente a la propiedad tomó unas pocas milésimas de segundos. Pero lo que sí resultó muy interesante fue la prueba accediendo dinámicamente a la propiedad:
dynamic test = new Test(); DateTime startTime = DateTime.Now; for (int i = 0; i < 1000000; i++) { test.Value = 0; } Console.WriteLine(DateTime.Now.Subtract(startTime).ToString());Para mi sorpresa, el ejemplo dinámico solamente tomó "00:00:00.1030059". A pesar que fué un poco más demorado que el acceso directo (obviamente), fué muchísimo más eficiente que el acceso por reflection. Si alguien tuvo duda de sus ventajas en algún momento (yo las tuve), espero que todo haya quedado aclarado.
Como todo tiene que ser con medida, tenemos que recordar que "dynamic" no está creado para usarlo en todas partes y en todo momento. Solamente es eficiente cuándo es estrictamente necesario, y su propia naturaleza puede introducir errores en nuestro código que no serán detectados en tiempo de compilación. Por lo demás, aquí tenemos algo más que logrará mejorar nuestró código en buena medida.
P.D: Java? Hello? Are you there? What are you waiting for?
Nice job as always Santi... you just spared me the trouble of having to read about this elsewhere.
ReplyDeleteSean, I hope your Spanish is still good enough to go thru all the article :)
ReplyDeletelol... Thankfully I still understand perfectly... Just finding it harder and harder to speak it.
ReplyDelete