CodeGym /Cursos /C# SELF /Modificadores de parámetros (

Modificadores de parámetros ( ref, out, in)

C# SELF
Nivel 12 , Lección 4
Disponible

1. Introducción

En C#, por defecto todos los parámetros de los métodos se pasan por valor, o sea, al método le llega una copia, no el original.

¿Pero qué pasa si necesitas cambiar una variable externa desde dentro del método? ¿O devolver varios valores desde un método? ¿O tal vez necesitas pasar una estructura grande al método, pero no quieres copiar todo ese "maletón sin asa", y te basta con pasarle una referencia para ahorrar memoria?

Todo esto va de los modificadores de parámetros. Te dan control sobre la "ruta de paso" de los datos entre el código que llama y el método.

Tipos de modificadores

Modificador Paso de datos ¿Hay que inicializar antes de llamar? ¿Se puede leer dentro? ¿Se puede escribir dentro? Escenarios típicos de uso
ref En ambos sentidos Paso de variable para leer y/o modificar
out Solo hacia fuera No No (antes de asignar) Sí (¡obligatorio!) Devolver varios valores desde un método
in Solo hacia dentro No Paso de estructuras grandes para "solo lectura" ahorrando memoria

No te preocupes, ahora vamos a ver cada uno de ellos. Es mucho más fácil de lo que parece.

2. Modificador ref: Pasamos por referencia: leemos y escribimos

Imagínate: le llevas una taza de café a tu colega y le dices — "Toma, puedes beber, calentar o incluso echarle lo que quieras". Tu colega puede hacer lo que quiera con ese café, y lo que quede (o no quede) — vuelve a ti. Así funciona ref.


void DoSomething(ref int x)
{
    x = x + 10; // Cambiamos el parámetro de entrada
}
        
Método con parámetro ref

Para pasar un parámetro como ref, tienes que declararlo con ese modificador tanto en la declaración como en la llamada al método:

int miNumero = 5;
DoSomething(ref miNumero);
Console.WriteLine(miNumero); // muestra 15

Tiene que ser sí o sí una variable, no puedes pasar una expresión por referencia. Este código no va a funcionar:

DoSomething(ref 10);		// ¡aquí tiene que ir una variable!
  • Antes de llamar: la variable tiene que estar inicializada (tiene que tener un valor asignado).
  • Dentro del método: puedes leer y cambiar el valor.
  • Después de llamar: los cambios se mantienen.

Ejemplo: Intercambiar valores de variables

A veces necesitas intercambiar los valores de dos variables. Pero ojo: en C# los tipos por valor (por ejemplo, int) por defecto se pasan por valor. Eso significa que al pasarlos a un método se copian los valores, no las referencias a las variables. Para que el método pueda cambiar las variables originales del código que llama, tienes que usar la palabra clave ref.

Ejemplo sin ref — no funciona:

// Intento de intercambiar valores — no funciona
void Swap(int a, int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(x, y); // Se pasan copias de los valores
Console.WriteLine($"{x}, {y}"); // → 10, 20 — ¡no cambió nada!

Dentro de la función Swap las variables a y b son copias de x y y. Solo cambiamos esas copias, los originales no se tocan.

Ejemplo usando ref — las variables se intercambian


// Intercambiamos valores usando ref
void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 10;
int y = 20;
Swap(ref x, ref y); // Se pasan referencias a las variables
Console.WriteLine($"{x}, {y}"); // → 20, 10 — todo bien cambiado
        

ref permite pasar referencias a las variables, no sus valores.
Por eso la función Swap trabaja directamente con x y y, cambiando su contenido.

¿Cuándo usar ref?

  • Cuando necesitas cambiar la variable que pasas (por ejemplo, incrementarla, reemplazarla).
  • No hace falta devolver un nuevo valor (como con return), porque quieres modificar el argumento directamente.
  • No lo uses para "sacar" un montón de valores distintos — para eso hay otro modificador.

3. Modificador out: Pasamos hacia fuera

Ahora imagina: vas a casa de tu colega, pero tienes la taza vacía, y le pides: "Llénala — con lo que quieras, café, té, lo que sea!" Después tu colega está obligado a llenarla con algo, si no el compilador se enfada.

out está hecho para que un método pueda devolver varios valores diferentes, sin usar el valor de retorno. Estos parámetros tienen que ser inicializados sí o sí dentro del método.


void GetCoordinates(out int x, out int y)
{
    x = 5;
    y = 10; // ¡Sin esto - error!
}
        
Método con parámetros out
  • Antes de llamar: la variable no hace falta inicializarla, incluso puedes poner simplemente out int x en la llamada.
  • Dentro del método: tienes que asignarle un valor antes de salir del método, si no el compilador se pone en huelga.
  • Después de llamar: la variable tiene el nuevo valor.

Ejemplo: Devolver dos valores a la vez desde un método

void ParseNameAndAge(string input, out string nombre, out int edad)
{
    string[] partes = input.Split(',');	// convertimos la cadena en array de strings - separamos por ','
    nombre = partes[0];
    edad = int.Parse(partes[1]);
}

string entradaUsuario = "Iván,25";
ParseNameAndAge(entradaUsuario, out string nombreUsuario, out int edadUsuario);
Console.WriteLine($"{nombreUsuario} — {edadUsuario} años");

¿Cuándo usar out?

  • Cuando necesitas devolver varios valores desde un método (por ejemplo, dividir una cadena en partes, como arriba).
  • Cuando el tipo de valor devuelto no está definido de antemano (por ejemplo, intentar convertir una cadena a número).

Ejemplos de .NET: El método int.TryParse(string, out int) — ¡un clásico!

string entrada = "123";
if (int.TryParse(entrada, out int numeroParseado))
{
    Console.WriteLine($"Convertido: {numeroParseado}");
}
else
{
    Console.WriteLine("¡Error de conversión!");
}

El método int.TryParse devuelve true si pudo convertir la cadena pasada a número y false si no pudo. El valor numérico lo devuelve a través de out.

Errores típicos y situaciones graciosas usando ref y out

  • Olvidaste inicializar la variable para ref — entonces el compilador se enfada.
  • No asignaste valor para out — el compilador se enfada aún más.
  • Olvidaste poner el modificador en la llamada: escribiste DoSomething(x) en vez de DoSomething(ref x).
  • ¡No se puede usar ref o out con constantes o literales! Solo pueden ser variables. Solo ellas tienen dirección en memoria y se puede hacer referencia a ellas.

4. Modificador in: Solo lectura, y solo por referencia

A veces pasa que tienes que pasar un objeto grande y complejo a un método. Normalmente ese objeto se pasa por referencia, pero entonces puede ser modificado por la función, que tiene acceso total — porque le diste la referencia.

En teoría podrías hacer una copia completa de tu objeto y pasarla. Así el original no se toca. Pero si el objeto es muy grande, estarías copiando datos a lo tonto. Mucho mejor sería pasar el objeto al método y pedirle al compilador que vigile que nadie lo cambie.

in — es un modificador nuevo que permite pasar estructuras por referencia, pero solo para lectura. Es como "exponer una espada legendaria tras un cristal": puedes mirar, pero no tocar.


void PrintPoint(in Point pt)
{
    pt.X = 5; // ¡Error! Solo lectura.
    Console.WriteLine($"Punto: {pt.X}, {pt.Y}");  //Esto sí se puede
}
        
Método con parámetro in (solo lectura)
  • Antes de llamar: la variable tiene que estar inicializada.
  • Dentro del método: solo puedes leer, no modificar.
  • Ahorro de memoria: la estructura no se copia, se pasa por referencia.

Se usa, por ejemplo, para arrays de estructuras grandes, para evitar copias innecesarias. Pero con clases (tipos por referencia) in no tiene mucho efecto.

Ejemplo: Pasar una estructura grande por referencia para no copiar

struct BigData
{
    public int A, B, C, D, E;
}

void PrintBigData(in BigData datos)
{
    datos.A = 10; // no se puede - solo lectura!
    Console.WriteLine(datos.A + datos.B + datos.C + datos.D + datos.E);
}

BigData misDatos = new BigData { A = 10, B = 20, C = 30, D = 40, E = 50 };
PrintBigData(in misDatos);

5. Preguntas que suelen preocupar:

¿Se puede usar ref/out/in con arrays y tipos por referencia?
¡Sí, se puede! Pero ojo: los arrays ya son tipos por referencia. Si pasas un array con ref, puedes cambiar la referencia en sí. Normalmente esto se usa más con estructuras.

¿Cuál es la diferencia entre ref y out, si puedo obtener el valor en ambos casos?
En ref la variable tiene que estar inicializada antes de usarla. En out no hace falta, pero dentro del método hay que asignarle un valor al menos una vez.

¿Qué pasa si intento usar un literal (por ejemplo, el número 5) con ref/out/in?
El compilador te da error al instante. ¡Solo variables!

¿Cuántos parámetros puedo poner como ref/out/in?
¡Los que quieras, no hay límite! Pero no te pases por la legibilidad del código: si tienes más de dos o tres, mejor piensa en devolver una tupla, estructura o clase.

1
Cuestionario/control
Clases y objetos, nivel 12, lección 4
No disponible
Clases y objetos
Introducción a la herencia
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION