Cuando se pasa una clase como parámetro a cualquier método de C# ésta se pasa siempre por refencia. Y a continuación paso a explicar esta "obviedad"...

Las clases son tipos por referencia por lo que cuando se pasan como parámetro a un método, cualquier cambio que se realice sobre ellas desde dentro del método (asignar una propiedad o cualquier otra cosa que cambie su estado), se conserva al terminar la llamada al método.

Sin embargo existe una palabra clave en el lenguaje llamada ref que sirve para pasar parámetros por referencia. ¿cuál es su objeto entonces si las clases se pasan por referencia siempre?

Su objeto es pasar por referencia los llamados "tipos por valor". Tipos por valor son las estructuras y los tipos básicos (int, bool, etc...). Éstos, salvo que se indique explícitamente, siempre se pasan por valor, es decir, se pasa una copia de los mismos a la pila para su procesamiento.

Consideremos el siguiente ejemplo:

class Persona
{
    public string Nombre = "Original";
}

....

void CambiaNombre(Persona p)
{
    p.Nombre = "Cambiado";
}

...

Persona per = new Persona();
CambiaNombre(per);
Console.WriteLine(per.Nombre);

En este ejemplo, al llamar a la función CambiaNombre pasándole un objeto de la clase Persona, lo que obtenemos en la consola es la plabara "Cambiado". Esto se debe a que el objeto se pasa por referencia.

Si cambiamos la definición de la clase persona y la convertimos en una estructura (que tiene las mismas capacidades que una clase, vale la pena recordar):

struct Persona
{
    public string Nombre;
}

Al ejecutar el mismo código de antes en la consola veríamos "Original", ya que una estrcutura es un tipo por valor y por lo tanto lo que se pasa como parámetro es una copia del objeto, no una referencia al objeto original.

La forma que proporciona el lenguaje para pasar referencias a tipos por valor a los métodos es la palabra clave ref. Al colocarla delante del parámetro el compilado envía una referencia a la estructura y no una copia, por lo que se conservan las moificaciones hechas dentro del método. En nuestro ejemplo bastaría con poner:

void CambiaNombre(ref Persona p)

y al llamar al método también:

CambiaNombre(ref per);

El problema de esto es que antes de pasar el parámetro hay que convertir el tipo por valor en un tipo por referencia, lo que se conoce técnicamente como operación de Boxing. Y al terminar la llamada se debe hacer un Unboxing que es la operación contraria. Dicho proceso es más costoso que simplemente pasar una referencia por lo que el rendimiento de la aplicación puee bajar si se hace con mucha frecuencia (dentro de un bucle largo, por ejemplo). Además, el propósito perseguido normalmente cuando se crea una estrcutura es precisamente usarla como tipo por valor, por lo que en la práctica la palabra clave ref se usa más bien poco.

Existe una palabr similar llamada out que también se puede poner en un parámetro para poder obtener referencias a objetos modificados y que tiene sentido incluso con clases. La diferencia entre refout es que en el primero el objeto que se pase al parámetro tiene que estar inicializado (es decir, tiene que ser una instancia concreta de un tipo) mientras que en el segundo caso puede ser simplemente una variable sin inicializar que a la vuelta de la función contendrá una clase concreta como valor de retorno.

💪🏻 ¿Este post te ha ayudado?, ¿has aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor