A menudo me enfrento con diferentes tipos de personas (es decir, gente común, ingenieros de sistemas, etc.) y veo que no tienen ni idea (y lo peor de todo es que se cierran en lo que creen o en lo que aprendieron y no están abiertos a nuevas posibilidades) de parámetros, tipos de valor, tipos de referencia, la clase String.
Mucha gente anda diciendo por ahí que String no es una clase. Algunos me hablan de "Data Type" (más tarde vamos a ver qué es realmente un Data Type). Otros le asocian una característica similar que la de int o bool (tipos primitivos) debido a que Visual Studio cuando estamos trabajando en C# pinta de azul la palabra "string" al igual que "int" o "bool". Para justificar todo esto que dicen ponen de ejemplo el siguiente código:
void HolaMundo()
{
string lHolaMundo = "Hola Mundo";
ChauMundo(lHolaMundo);
Console.WriteLine(lHolaMundo);
}
void ChauMundo(string pCadena)
{
pCadena = "Chau Mundo";
}
En la línea 05 la salida es "Hola Mundo" y no "Chau Mundo".
Primero voy a empezar con algunos conceptos:
String (Clase): String es una clase. El valor del objeto String es el contenido de la colección secuencial y ese valor es inmutable. Un objeto String se denomina inmutable (de sólo lectura) porque no se puede modificar su valor una vez que se ha creado. Los métodos que aparentemente modifican un objeto String devuelven en realidad un nuevo objeto String que contiene la modificación. Si realmente es necesario modificar el contenido de un objeto de tipo cadena, hay que utilizar la clase System.Text.StringBuilder.
Palabras clave de C#: bool, byte, ..., object, string... Estas palabras claves son alias para tipos del .NET Framework. Es lo mismo bool que System.Boolean, o string que System.String. Que Visual Studio pinte de azul o del color que sea por igual a bool y a string no significa que sean diferentes a System.Boolean o a System.String o que String sea un tipo de valor. Lo que importa es el CTS (Common Type System) que es por así decirlo una de las cosas que un lenguaje de programación que quiera trabajar con .NET debe cumplir.
Object.Equals (Object, Object): Determina si las instancias de Object especificadas se consideran iguales.
Vamos a modificar un poco el código ejemplo:
string mHolaMundo = "Hola Mundo";
void HolaMundo()
{
ChauMundo(mHolaMundo);
Console.WriteLine(mHolaMundo);
}
void ChauMundo(string pCadena)
{
VerificarReferencias(mHolaMundo, pCadena);
pCadena = "Chau Mundo";
VerificarReferencias(mHolaMundo, pCadena);
}
void VerificarReferencias(object pObjeto1, object pObjeto2)
{
if (object.Equals(pObjeto1, pObjeto2))
{
Console.WriteLine("Misma referencia");
}
else
{
Console.WriteLine("Distinta referencia");
}
}
En la línea 10, cuando se llama al método VerificarReferencias la salida es "Misma referencia". Esto significa que las variables mHolaMundo y pCadena hacen referencia al mismo objeto. En la línea 11, la variable pCadena va a pasar a tener como referencia un nuevo objeto de tipo String, pero no va a modificar la variable mHolaMundo ni el objeto al que hace referencia mHolaMundo. Podemos comprobar esto en la línea 12 viendo que la salida es "Distinta referencia". Si en la línea 11 se hubiera hecho:
La variable pCadena iba a quedar apuntando a null, pero mHolaMundo iba a seguir igual (esto es un comportamiento general, no solo de la clase String).
Vamos a ver lo siguiente:
class Contenedor
{
public string Cadena;
}
Contenedor mContenedor = new Contenedor();
void HolaMundoContenedor()
{
mContenedor.Cadena = "Hola Mundo";
ChauMundoContenedor(mContenedor);
Console.WriteLine(mContenedor.Cadena);
}
void ChauMundoContenedor(Contenedor pContenedor)
{
pContenedor.Cadena = "Chau Mundo";
}
En este ejemplo en la línea 12 la salida va a ser "Chau Mundo". Esto es porque mContenedor y pContenedor tienen como referencia el mismo objeto y lo que estamos cambiando es la información a la que apuntan las referencias. Lo que se modifique "interno" en pContenedor (ejemplo el valor de un miembro de clase) se va a ver reflejado en mContenedor. Aún así, si hacemos en la línea 17 lo siguiente:
La variable mContenedor no va a quedar en null. Razón: por defecto los parámetros se pasan por valor. Si hiciéramos a pContenedor un parámetro por referencia al hacer pContenedor = null; mContenedor automáticamente quedaría en null.
class Contenedor
{
public string Cadena;
}
Contenedor mContenedor = new Contenedor();
void HolaMundoContenedor()
{
mContenedor.Cadena = "Hola Mundo";
ChauMundoContenedor(ref mContenedor);
Console.WriteLine(mContenedor.Cadena);
}
void ChauMundoContenedor(ref Contenedor pContenedor)
{
pContenedor = null;
}
El ejemplo va a tirar una excepción.
En la próxima parte voy a hablar de parámetros. Esto es una simple introducción al comportamiento de pasar parámetros por valor y por referencia con algunas aclaraciones de la clase String y las palabras claves de C#.