Dinámico, estático, ¿cuál es la diferencia?
En el contexto de este artículo, dinámico y estático se puede
aplicar de dos formas, uno haciendo referencia a los tipos de datos
y otro a los lenguajes.
Cuando se habla de dinámico y estático haciendo referencia a los
tipos de datos, estático significa que los tipos de datos solo
soportan los elementos que ellos definen; es decir, solamente
podemos acceder a los métodos, propiedades, etc., que estén
definidos en el tipo. Por otro lado, dinámico significa que sin
necesidad de conocer de antemano el tipo de datos que estamos
usando, podamos acceder a cualquiera de los miembros (métodos,
propiedades, etc.) que ese tipo defina (no confundamos esto con el
polimorfismo, que si bien se puede parecer no es exactamente lo
mismo). Aunque otra interpretación puede ser que esos tipos
dinámicos no son específicos, sino que un mismo tipo (que en
realidad ni es un tipo) soporta cualquier valor, algo parecido al
tipo Variant de COM (o de VB6). Y por último, si ese tipo dinámico
es producido por un lenguaje orientado a objetos, posiblemente sea
una referencia a un objeto de un tipo que se ha inferido según el
valor asignado, algo parecido a lo que hacen VB.NET y C# con la
inferencia de tipos; la diferencia estará que los lenguajes
estáticos habrán definido esas variables con un tipo de datos,
mientras que los lenguajes dinámicos no suelen tener la necesidad
(ni la obligación) de definir las variables.
Cuando dinámico y estático se aplican a los lenguajes (para
simplificar), estático significa que el lenguaje utiliza tipos
estáticos, es decir especificaciones concretas para cada tipo de
datos, mientras que los lenguajes dinámicos no necesitan definir
esos tipos de datos, ya que suelen inferirse y el intérprete (los
lenguajes dinámicos suelen tratarse como los lenguajes de script, y
éstos suelen ser interpretados en lugar de compilados) inferirá el
tipo según el valor asignado.
En el resto del artículo nos centraremos en los tipos de datos más
que a los lenguajes.
Visual Basic y los tipos dinámicos
A los lectores que conozcan o hayan usado alguna versión de Visual
Basic (da igual si es para .NET o no), todo lo comentado
seguramente les sonará de algo. Simplificando, podríamos decir que
el acceso dinámico a los miembros de un objeto es lo que llaman en
inglés late binding (enlace tardío) y el acceso estático lo que se
conoce como early binding (enlace temprano), algo que en Visual
Basic siempre ha existido.
El enlace estático (o temprano) solo permite acceder a los
miembros "conocidos" del tipo, es decir, los miembros que esa clase
(o tipo) defina; esto está bien y es lo que siempre nos han
recomendado (cuando los que ahora lo recomendamos estábamos
aprendiendo), entre otras cosas porque la ejecución es más rápida y
sobre todo menos propensa a errores. Incluso hay lenguajes como C#
que solo permiten esta forma de enlace, al menos hasta ahora, ya
que su próxima versión (la 4.0) sí que soportará el enlace dinámico
además del estático.
Por otra parte, el enlace dinámico (o tardío) nos permite hacer
referencia a miembros que no existen en el tipo de datos que
estamos usando. Para que esto sea posible, el tipo debe ser de uso
general (en .NET de tipo Object, ya que todos los tipos de .NET se
derivan de ese tipo básico). Es decir, el compilador aceptará el
acceso a ese miembro que no está definido en el tipo de datos y
será en tiempo de ejecución cuando se compruebe si ese miembro está
realmente definido; por eso lo de "enlace tardío", ya que hasta que
no se ejecute ese código no se podrá comprobar el tipo de datos que
realmente tiene ese "objeto". Aclarar que actualmente este tipo de
enlace tardío en los lenguajes de .NET solo se permite en Visual
Basic, pero no en C#, aunque esto cambiará en la próxima
versión.
En el listado 1 podemos ver cómo acceder a un método inexistente
en la clase Object, pero que en realidad el objeto referenciado por
la variable obj1 sí que define, y por tanto este código funcionará
perfectamente. La salvedad es que si estamos usando Visual Basic
2008, además de usar Option Strict Off, también deberíamos
desactivar la inferencia automática de tipos, con idea de que por
defecto las cosas se hagan como se deben hacer (definiendo
expresamente la variable del tipo de datos que va a contener); por
tanto, lo mejor es definir expresamente la variable obj1 como de
tipo Object. El método Mostrar usado desde la variable obj1 está
definido en la clase MiClase, por tanto la llamada a ese método
funcionará cuando se ejecute la aplicación.
Si el lector piensa que lo correcto hubiera sido declarar la
variable obj1 del tipo MiClase, decirle que estaría en lo cierto,
pero aquí he usado esa clase para que la variable tuviera algo, ya
que lo habitual es que ese valor se obtuviera de alguna forma en la
que no tengamos forma de comprobar que en realidad tiene ese valor,
como podría ser si esa variable se asignara mediante una llamada a
CreateObject, tal como vemos en el listado 2.
La desventaja de usar el enlace tardío radica en el hecho de que
el compilador no hace ninguna comprobación de que ese miembro
realmente esté definido en el tipo de datos, y por tanto es posible
que el código falle en tiempo de ejecución, y ya sabemos que no hay
nada peor que nuestra aplicación falle cuando esté ejecutándose en
el equipo de nuestros clientes.
Por otro lado, hay situaciones en las que puede ser útil usar ese
enlace dinámico, normalmente cuando usamos otros objetos que no
proceden del propio .NET Framework, por ejemplo si usamos objetos
COM procedentes de aplicaciones como Office, tal como acabamos de
ver en el listado 2.
Visual Basic puede ser no estricto, pero no es dinámico
Tal como están las cosas actualmente, y de forma predeterminada
(algunos seguimos lamentando que esto sea así), Visual Basic no es
estricto a la hora de hacer conversiones o de acceder a los
miembros de un objeto; es decir, se puede utilizar el enlace tardío
a la hora de acceder a los miembros que no estén definidos
expresamente (estáticamente) en un tipo en particular. Pero esto no
significa que Visual Basic sea dinámico en el aspecto de permitir
acceder dinámicamente a los miembros de un objeto, sino que lo
simula y a la larga se obtienen los mismos resultados.
La única forma de permitir que Visual Basic simule ser un lenguaje
dinámico (en lo referente a acceder dinámicamente a los miembros de
un objeto) es desactivando Option Strict. El problema es que esto
es algo que muchos no recomendamos que se haga, y esperemos que no
se justifique el hacerlo por la necesidad de acceder dinámicamente
a los miembros de un tipo del que no tenemos la información de los
miembros que expone públicamente.
Ámbitos de Option Strict
Afortunadamente, Visual Basic nos permite usar Option Strict (ya
sea activado o desactivado) a dos niveles, y el nivel más bajo (o
reducido) en el que se puede utilizar es a nivel de fichero de
código; es decir, podemos restringir la comprobación no estricta
solo en el código que esté definido dentro de un fichero de código.
Esto, unido a que podemos definir clases parciales, nos permite
definir en esos ficheros de código solo aquellas partes de nuestros
tipos que necesiten que la comprobación estricta del código no esté
activada, ya que es esa comprobación estricta que se hace al tener
activado Option Strict la que no nos permite acceder a miembros de
un objeto que no estén definidos en el tipo de ese objeto, o sea
cuando usamos lo que ahora se llama acceso dinámico.
El otro ámbito de Option Strict es a nivel de proyecto, ya que
podemos indicar que, por ejemplo, cierto estado esté activado en
todo el proyecto. De esta forma, si no indicamos expresamente el
estado de esta opción de comprobación estricta, siempre estará
activado o desactivado, dependiendo del valor que hayamos dado en
las opciones del proyecto. Tal y como hemos visto en el párrafo
anterior, cuando necesitemos desactivar esa comprobación estricta
lo podremos hacer a nivel de fichero de código; de esa forma,
solamente en ese fichero se dejará de hacer las comprobaciones que
el compilador hace cuando asignamos el valor On a Option
Strict.
Recapitulando sobre Option Strict
A título de recordatorio (así valdrá para aquellos lectores que no
suelen utilizar Visual Basic y por tanto puede que no sepan para
qué sirve esta instrucción) activando Option Strict el compilador
nos obligará a definir todas las variables con un tipo de datos
adecuado; además, a la hora de asignar un valor a esas variables se
comprobará que el tipo de datos asignado es el mismo que el de la
variable que recibe el valor o se puede convertir de forma
implícita (automáticamente). Y cuando estamos asignando valores
entre variables de distintos tipos de datos, también se comprobará
si se puede hacer de forma implícita o se necesita hacer una
conversión explícita (cast); en este último caso, la presencia en
el código de esa conversión explícita nos pondrá en alerta de que
es posible que dicha conversión falle y por tanto tengamos cuidado
con ese código, ya que según la Ley de Murphy, si algo puede
fallar, seguro que fallará.
Cuando instalamos Visual Studio (o Visual Basic Express) el valor
predeterminado de Option Strict es Off, es decir, desactivado; por
tanto, todas estas comprobaciones que he comentado antes no se
hacen. De esa forma, podemos declarar variables sin necesidad de
indicar de qué tipo son y asignarles valores de cualquier tipo.
Esto es posible ya que esas variables en realidad son de tipo
Object y este tipo de datos acepta cualquier valor; por tanto,
podremos asignar cualquier valor a una variable que sea de tipo
Object.
¿Qué problema hay al usar Option Strict Off?
Problema, lo que se dice problema, no hay ninguno. De hecho, mucha
gente piensa que incluso es mejor tener desconectada esa opción, ya
que así no tenemos que preocuparnos en hacer conversiones entre
tipos ni tener que declarar las variables con un tipo de datos
determinado. Pero no nos engañemos: está demostrado que una
programación con tipos de datos concretos es más eficiente que una
que utilice tipos de datos más generalizados (por no decir
"genéricos", que se podría confundir con los tipos generic), y todo
el trabajo que nos dará tener que hacer las conversiones de forma
explícita nos ayudará a saber qué es lo que estamos haciendo y
(como dije antes) alertarnos de que algo puede ir mal al hacer la
conversión. Y es que el compilador de Visual Basic a la hora de
hacer las conversiones entre tipos diferentes de datos lo hará casi
de la misma forma que lo haremos nosotros, pero será más difícil de
saber que se puede producir un fallo al convertir entre esos dos
valores si no vemos que ahí se está haciendo una conversión.
Los tipos dinámicos en .NET Framework 4.0
Aunque aún está en una fase muy temprana de desarrollo (a la hora
de escribir este artículo solo está disponible la primera CTP),
.NET Framework 4.0 incluirá soporte para los tipos dinámicos, ya
que también permite mediante el DLR la integración con lenguajes
dinámicos y por tanto, se podrá intercambiar información entre esos
lenguajes y los denominados estáticos.
Para permitir la utilización de los tipos dinámicos en lenguajes
como C# (que son muy estrictos), se ha tenido que hacer uso de una
nueva forma de definir esos tipos dinámicos, ya que el compilador
no debe comprobar si los miembros que se aplican a ese objeto están
definidos o no; al menos, esa comprobación no se hará en tiempo de
compilación, si no que será en tiempo de ejecución cuando se
compruebe si realmente el objeto asignado a esa variable dinámica
soporta ese método o propiedad que se está usando. En C# 4.0 se
definirá un tipo dinámico con la palabra clave dynamic; en cuanto
el compilador se encuentre con esa declaración, sabrá que debe
aplicar late binding a esa variable, y por tanto no hacer ningún
tipo de comprobación hasta que se esté ejecutando el código.
En el listado 3 vemos un ejemplo parecido al del listado 1, solo
que en esta ocasión estamos usando código de C# 4.0 y
particularmente de la instrucción dynamic y lo que el uso de esa
instrucción supone: enlace tardío.
En Visual Basic no se ha añadido ninguna nueva instrucción para
definir este tipo de variables dinámicas, ya que Visual Basic
permite usar ese modo "retardado" de comprobación si se utiliza
Option Strict Off.
Pero no nos confundamos: eso no es un tratamiento dinámico de los
tipos de datos, ya que en realidad, para permitir que todo esto sea
posible, se ha agregado una nueva interfaz para definir estos tipos
dinámicos; esa interfaz es IDynamicObject, que está definida en el
espacio de nombres System.Scripting.Actions, y Visual Basic no
utiliza esta interfaz, sino que simplemente hace lo que ha estado
haciendo durante muchos años: esperar a que llegue el momento de la
ejecución del código, y en ese preciso momento es cuando comprueba
si el objeto define o no ese método (o cualquier otro miembro al
que queramos acceder). Si lo define, bien; si no lo define, mal y
de regalo una excepción.
¿Será Visual Basic un lenguaje dinámico?
Tal como están las cosas, la respuesta es no. Al menos en lo que
se refiere a los tipos dinámicos, ya que la intención es que llegue
a serlo, es decir, que vuelva a ser un lenguaje de script o casi,
ya que ahora a ese tipo de lenguajes se les llama dinámicos. Pero
todo esto lo comprobaremos más adelante, cuando la próxima versión
de Visual Basic (y de .NET Framework) esté más madura. Mientras
tanto, contentémonos con lo que tenemos y aprovechémoslo; en esta
isla seguiré explicando cosas para aprovechar mejor este
lenguaje.
Conclusiones
En este artículo hemos hablado sobre algo que en los próximos
meses (o años) seguramente será algo más habitual: los tipos y
lenguajes dinámicos. Pero como hay que estar en la realidad,
también hemos visto (aprovechando la coyuntura del tema) cómo
Visual Basic puede ser dinámico o lo que es lo mismo, menos
estricto con los tipos de datos que utilizamos en nuestras
aplicaciones. Confío en que el lector sepa con más certeza que esa
forma de dinamismo no es la más recomendable, salvo que realmente
sepamos qué es lo que estamos haciendo, y en que después de esta
lectura el lector sabrá qué es lo que hace en cada momento, o al
menos sabrá cómo ser estricto a la hora de escribir el código o no
serlo, si así lo considera oportuno.