Archivo de la etiqueta: Tipos

Los datos (por referencia)


Como decíamos en la publicación pasada, los datos simples, se pueden almacenar en tipos por valor. Pero si la cosa se complica un poco, entonces resulta imposible reservar el espacio o equipararlo a registros del procesador.

Tomemos, por ejemplo, las cadenas de caracteres: Si estuviésemos hablando de un caracter (el tipo char), ocupa lo mismo que un byte (en realidad, que 2, dados los caracteres especiales de idiomas no latinos). Cada letra, número o símbolo, se representa por una combinatoria de bits hay estipulada en tablas predefinidas. (Caracteres ANSI, ASCII, Windows.. hay varios ).

PERO, si queremos almacenar palabras (imprescindible), el tema se complica.

Antiguamente, se utilizaban tipos de datos declarados como variables de longitud fija (y lo que sobra, a llenarlo con espacios).

Además de una perdida de espacio, resulta bastante poco eficiente para, por ejemplo, editores de texto, aún cuando diccionarios, listas de palabras etc. .., podrían aprovecharse de ese tamaño fijo para proceder con ordenamientos o búsquedas más rápidas. Pero por lo demás, resulta imposible de administrar en todos los usos posibles.

Así, se define teóricamente una cadena de caracteres como… bueno, casi eso. Un arreglo, vector, matriz o tabla (array) de caracteres. Cada una, de su propia cantidad de elementos (dependiendo de las letras a almacenar).

EL procesador no se “copia” el dato… usa un puntero hacia donde se encuentre dicha cadena en memoria y utiliza la misma… asunto resuelto…

Hasta que hay que agregar caracteres. La memoria se reserva para los caracteres que contiene la variable. Si agrego caracteres, “más allá” de lo previsto puedo reemplazar parte de la memoria de otras cosas.. (¿Les suena “se ha producido un error de protección general y el programa se apagará?.. eso es, cuando “el Windows se cuelga” es porque alguien no controló este tipo de cuestiones).

Incrementar, reemplazar partes, reducir cadenas de caracteres es una operación de 3 pasos: copio la original en otra, insertando donde sea, cambio el puntero a la nueva dirección de memoria, y descarto el espacio anterior (o le digo al sistema, sencillamente, que puede usarlo).

Vamos, se complica. Y si encima, empezamos a definir tipos de datos más complejos, con mayor razón se deben operar como referenciados. Es lo que sucede con todos los otros tipos de datos.

Y si lo vemos desde la perspectiva del .Net Framework, hay algunas otras definiciones a tener en cuenta.

Dado que en el .Net Framework se utiliza mucho la herencia, (de la cual hablaremos más adelante), todos los tipos de datos tienen relación entre sí. De momento, digamos en general que, todo dato (tipo desde ahora) que se pueda almacenar hereda de un tipo súper básico: objeto (object).

Esto es tan así, que hasta los tipos por valor, que hablamos anteriormente, en la definición del .Net Framework, heredan de object.

Es por eso que todos los tipos de datos poseen algunos métodos que son comunes.

clip_image001

Y vamos a definir cada uno de estos métodos, para ir entendiendo como se hacen las cosas en .Net.

Equals

Compara el objeto actual con otro para evaluar si son iguales. Sin embargo, depende del tipo de dato que estemos hablando.

En el caso de los tipos por referencia, la comparativa se realiza utilizando ReferenceEquals.

Si los tipos son por valor, lo que se comparan son los valores.

GetHashCode

Obtiene el valor identificativo único de una instancia, específicamente pensado para ser manipulada dentro de una tabla o lista hash (como podría ser un diccionario)

Esto implica que no se puede confiar en este valor como identificativo único en el tiempo, ni entre aplicaciones. En realdad, salvo para el caso puntual de las listas mencionadas, no debiera considerarse dicho valor como útil.

GetType

Existe un tipo de dato en el .Net Framework que permite identificar cualquier otro. Esto es, cada tipo de datos es de un Tipo, y por lo tanto, hay un Type que lo representa.

El método GetType, nos permite obtener el tipo que define esta instancia en particular.

A su vez, el Type obtenido, nos permite identificar los miembros (Propiedades, campos, métodos, eventos) que ese tipo implementa.

MemberwiseClone

Crea una copia “superficial” del objeto en otro.

En tipos por valor, copia el valor bit a bit.

En tipos por referencia, copia el valor de la referencia, bit a bit… o sea que ambos apuntan a la misma instancia.

Si lo que se quiere es hacer una copia independiente (también llamada profunda), es necesario transferir todos los valores desde el origen al destino.

Algunos tipos implementan un método específico (CopyTo), para esto.

ReferenceEquals

Establece si el objeto referencia a la misma posición de memoria del objeto pasado como argumento. Así, sabremos exactamente si son el mismo, o no, aun cuando los valores de todas sus propiedades sean idénticos.

ToString

Genera una representación en cadena de caracteres del contenido del objeto. En muchos casos, lo que se retorna es el valor de dicha variable, representado como cadena (como es el caso de los tipos por valor en general). En cambio, en otros, la mayoría de los tipos por referencia, se retorna el nombre del tipo.

Sin embargo, muchos exponen formas complejas como ser tipo y valor principal… Esto depende de cómo se haya diseñado el objeto.

Pero, en sí mismos, se asume que el método ToString siempre retorna un valor significativo.

Así entonces, toda tipo, (insisto inclusive los que son por valor), heredan, directa o indirectamente, de Object y, por tanto, comparten estos métodos. Y aquí empieza la “magia” de esta historia.

Tenemos mucho que ver respecto de las herencias.

Hasta la siguiente.