domingo, 3 de abril de 2016

Tipos referencia, tipos valor y paso de parámetros en .net

Ya llevo varias conversaciones con distintos compañeros del trabajo sobre los tipos en .net y su y como se comportan en pasos por parámetro. Así que aquí estoy escribiendo este post.


En .Net hay tres tipos: tipo valor (value type), tipo referencia (reference type) y punteros (pointer type).
No voy a hablar de los punteros, ya que solo se pueden usar en contextos no seguros (unsafe) y no es algo que solemos hacer.

Las variables de los tipos de referencia contienen una referencia que apunta a los datos, mientras que los tipos valor contienen directamente los datos.
Los tipos referencia son los tipo class, interface, delegate y arrays
Los tipo valor son todos los tipos numéricos, boolean, char, date, struct y los enumerados.
Veamos un ejemplo para explicarlo mejor:

    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder("Hola"); //tipo referencia
        int i = 1; //tipo valor

        StringBuilder sb2 = sb; 
        sb2.Append(" mundo"); 

        int j = i; 
        j++; 

        Console.WriteLine($"{sb} {i}");
        //Imprime "Hola mundo 1"

        sb2 = new StringBuilder();
        Console.WriteLine(sb);
        //Imprime "Hola mundo"
        Console.WriteLine(sb2);
        //Imprime ""
    }

Empecemos por las variables de tipo referencia. Cuando se igualan las variables sb y sb2, lo que se hace es que las dos variables apunten a al mismo objeto. Así que cuando se invoca el método append desde la variable sb2 se está modificando el objeto referenciado tanto por la variable sb como sb2, por eso al imprimir sb pone “Hola Mundo”. Pero cuando se modifica el valor de la variable sb2 con un nuevo objeto, no se esta modificando el valor de la variable sb que sigue apuntando al objeto original.En cuanto a las variables de tipo valor, como su propio nombre indican ellas mismas contienen el valor, así que cuando escribimos j = i lo que estamos haciendo es copiar el valor de la i en la j, pero si modificamos el valor de la j en ningún momento estamos afectando a la i.

Paso de parámetros

Veamos ahora qué sucede cuando pasamos las variables como parámetros a una función.
Por defecto en .Net los parámetros se pasan por valor, es decir que se copian las variables a otras del mismo tipo, de la misma manera que cuando definimos una variable del mismo tipo y la asignamos con un igual, es decir, igual que en el ejemplo anterior, pero con llamadas a funciones, por ejemplo

    static void Modifica(StringBuilder param, int x)
    {
        param.Append(" Mundo");
        x++;
    }

    static void Reasiga(StringBuilder param, int x)
    {
        param = new StringBuilder();
        x = 0;
    }

    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder("Hola"); //reference type
        int i = 1; //value type

        Reasiga(sb, i);
        Console.WriteLine($"{sb} {i}");
        //Imprime "Hola 1"

        Modifica(sb, i);
        Console.WriteLine($"{sb} {i}");
        //Imprime "Hola mundo 1"

        Console.ReadLine();
    }

Otra forma de pasar parámetros es por referencia, esto se hace anteponiendo ref tanto en la definición de la función como en el parámetro a pasar. en estos casos no se hace una copia de la variable, se pasa la propia variable, veamos qué ocurre en el ejemplo anterior si pasamos como referencia: 

    static void Reasiga(ref StringBuilder param, ref int x)
    {
        param = new StringBuilder();
        x = 0;
    }

    static void Modifica(ref StringBuilder param, ref int x)
    {
        param.Append(" mundo");
        x++;
    }

    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder("Hola"); //reference type
        int i = 1; //value type

        Reasiga(ref sb, ref i);
        Console.WriteLine($"{sb} {i}");
        //Imprime " 0"

        Modifica(ref sb, ref i);
        Console.WriteLine($"{sb} {i}");
        //Imprime " mundo 1"

        Console.ReadLine();
    }

Como se puede ver los valores devueltos son distintos, ya que se están pasando las propias variables y no una copia, con lo que tanto las reasignaciones, como cambios de valor afectan a todos los tipos.
Otra palabra reservada para pasar parámetros es out. Con out el parámetro se comporta igual que ref, pero la variable que se pasa no tiene que estar inicializada, con lo que obliga a hacer una asignación dentro de la función siempre. Así que la función “Modifica” no puede usar out, pero sí ref.

A nivel de sobrecarga de funciones, podemos tener dos funciones que se llamen igual, con los mismos parámetros una que le pasamos los parámetros por valor y una segunda por referencia u out.

No hay comentarios:

Publicar un comentario