¿Por qué utilizar la palabra clave 'ref' al pasar un objeto?

Resuelto Ryan asked hace 16 años • 11 respuestas

Si paso un objeto a un método, ¿por qué debería utilizar la palabra clave ref? ¿No es este el comportamiento predeterminado de todos modos?

Por ejemplo:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

La salida es "Bar", lo que significa que el objeto se pasó como referencia.

Ryan avatar Oct 09 '08 18:10 Ryan
Aceptado

Pase a refsi desea cambiar cuál es el objeto:

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef x)
{
  x = new TestRef();
  x.Something = "Not just a changed TestRef, but a completely different TestRef object";
}

Después de llamar a DoSomething, tno se refiere al original new TestRef, sino que se refiere a un objeto completamente diferente.

Esto también puede resultar útil si desea cambiar el valor de un objeto inmutable, por ejemplo, un string. No puede cambiar el valor de a stringuna vez que se ha creado. Pero usando a ref, podrías crear una función que cambie la cadena por otra que tenga un valor diferente.

No es una buena idea usarlo refa menos que sea necesario. El uso refle da al método libertad para cambiar el argumento por otra cosa; las personas que llaman al método deberán estar codificadas para garantizar que manejan esta posibilidad.

Además, cuando el tipo de parámetro es un objeto, las variables del objeto siempre actúan como referencias al objeto. Esto significa que cuando refse utiliza la palabra clave, se obtiene una referencia a una referencia. Esto le permite hacer cosas como se describe en el ejemplo anterior. Pero, cuando el tipo de parámetro es un valor primitivo (por ejemplo int), si este parámetro se asigna dentro del método, el valor del argumento que se pasó se cambiará después de que el método devuelva:

int v = 1;
Change(ref v);
Debug.Assert(v == 5);
WillNotChange(v);
Debug.Assert(v == 5); // Note: v doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}
Scott Langham avatar Oct 09 '2008 11:10 Scott Langham

Debe distinguir entre "pasar una referencia por valor" y "pasar un parámetro/argumento por referencia".

He escrito un artículo razonablemente largo sobre el tema para evitar tener que escribirlo con cuidado cada vez que aparece esto en los grupos de noticias.

Jon Skeet avatar Oct 09 '2008 12:10 Jon Skeet

En .NET, cuando pasa cualquier parámetro a un método, se crea una copia. En tipos de valor significa que cualquier modificación que realice en el valor está en el alcance del método y se pierde cuando sale del método.

Al pasar un Tipo de Referencia también se hace una copia, pero es una copia de una referencia, es decir ahora tienes DOS referencias en memoria al mismo objeto. Entonces, si usa la referencia para modificar el objeto, éste se modifica. Pero si modifica la referencia en sí (debemos recordar que es una copia), cualquier cambio también se perderá al salir del método.

Como se ha dicho antes, una asignación es una modificación de la referencia, por lo que se pierde:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

Los métodos anteriores no modifican el objeto original.

Una pequeña modificación de tu ejemplo.

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }
Ricardo Amores avatar Oct 09 '2008 12:10 Ricardo Amores

Dado que TestRef es una clase (que son objetos de referencia), puede cambiar el contenido dentro de t sin pasarlo como referencia. Sin embargo, si pasa t como referencia, TestRef puede cambiar a qué se refiere la t original. es decir, haz que apunte a un objeto diferente.

Ferruccio avatar Oct 09 '2008 11:10 Ferruccio

Con refpuedes escribir:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

Y t se cambiará una vez que se haya completado el método.

Rinat Abdullin avatar Oct 09 '2008 11:10 Rinat Abdullin