2009-07-20 8 views
10

Todavía estoy confundido acerca de pasar por ref.Pasando por ref?

Si tengo un objeto Cache al que quiero acceder/disponible para varios objetos, y lo inyecto usando la inyección de constructor. Quiero que afecte al único objeto de caché que he creado. p.ej.

public class Cache { 

    public void Remove(string fileToRemove) { 
     ... 
    } 
} 

public class ObjectLoader { 

    private Cache _Cache; 

    public ObjectLoader(Cache cache) { 

    } 

    public RemoveFromCacheFIleThatHasBeenDeletedOrSimilarOperation(string filename) { 
     _Cache.Remove(fileName); 
    } 
} 

¿Debo estar utilizando ref cuando paso la caché en el constructor de ObjectLoader?

Respuesta

11

No, no es necesario utilizar la palabra clave ref en esta situación.

Cache es una clase, es un tipo de referencia. Cuando se pasa una referencia a un método, se coloca una copia de la referencia (no del objeto en sí) en su parámetro. Ambas referencias, dentro y fuera, del método apuntan al mismo objeto en el montón, y la modificación de los campos del objeto usando uno se reflejará en el otro.

Al agregar ref a su método, las pases de llamada en la referencia original. Esto es útil en una situación en la que estarías reasignando (es decir,llamando al new) la ubicación a la que apunta la referencia dentro de un método de llamada.

7

Utilice la palabra clave 'ref' cuando necesite modificar a lo que apunta la referencia. Cuando pasa un tipo de referencia a un método, es pasado por valor, pero el valor es copia de esa referencia que se pasa al método. Esto significa que puede cambiar el estado general (es decir, propiedades/campos) del objeto referido, pero si intenta cambiar los puntos de referencia solo afectará a la copia.

Por ejemplo, dado que este método ...

private void Foo(MyClass obj) 
{ 
    obj = new MyClass(); 
    obj.SomeProperty = true; 
} 

Podemos pasar en el argumento y luego ver si se ve afectada:

MyClass test = new MyClass(); 
test.SomeProperty = false; 
Foo(test); 
Console.WriteLine(test.SomeProperty); // prints "False" 

Ahora, si hubiéramos definido el método que utiliza la palabra clave 'ref' ...

private void Foo(ref MyClass obj) 
{ 
    obj = new MyClass(); 
    obj.SomeProperty = true; 
} 

la salida sería "Verdadero", debido a que la referencia real se pasa al método, no una copia Cambiamos a qué apunta esa referencia dentro de la función y vemos los efectos de esos cambios.

Usted acaba de crear un nuevo puntero al objeto en el montón cuando omite la palabra clave 'ref'. Si cambia un puntero, no cambiará el otro.

...

Así que, para responder a su pregunta; no, no necesita utilizar la palabra clave 'ref' para cambiar el estado de su único objeto de caché cuando se pasa a un método.

+0

Es mucho más simple con los punteros C/C++ normales :) – Eugene

+1

No estoy de acuerdo :) Pero para las personas c/C++ es la diferencia entre * (o &) y ** :) – Stormenet

+1

nitpick: segundo ejemplo - usted es acceder a una propiedad estática. – Cherian

-3

Los objetos se pasan automáticamente por referencia, incluso cuando se declara que se pasan por valor en los argumentos de la función, en .NET Framework.

Esto se debe a que el objeto en sí es un tipo de referencia, por lo que puede modificar los miembros del objeto, aunque no pueda reemplazar el objeto en sí.

Ver

http://msdn.microsoft.com/en-us/library/aa903253(VS.71).aspx

+6

Los objetos se pasan por valor, como todo lo demás. La diferencia con los objetos (lecturas: tipos de referencia) es que la REFERENCIA se pasa por valor. –

+2

Todos los parámetros se pasan por valor en C# a menos que use explícitamente ref o out. Sin embargo, en el caso de los tipos de referencia, la referencia copiada sigue apuntando a la misma instancia para que pueda modificar la instancia, pero no la referencia en sí misma. –

1

Creo que se está preguntando cuántas copias del objeto Cache se crearán. Solo desea que una copia sea compartida por varios objetos de cliente. Bueno, hay una regla muy simple que puedes recordar en C#, siempre que quieras saber cuántas copias separadas de tu objeto se crearán.

Si el tipo de objeto se declara con la palabra clave class, entonces hay una sola manera de hacer una nueva instancia de que: con la palabra clave new.

Existen pequeñas excepciones a esto: puede llamar a los métodos BCL que crean objetos, pero el punto es que es explícito. Tienes que pedir específicamente que suceda. El idioma no hará automáticamente copias de los objetos class.

Así que en tu ejemplo, que tienen un class llamados Cache, y por lo que sabemos con certeza que se puede pasar alrededor de variables de tipo Cache tanto como te gusta, y no se crearán más copias de Cache. Todas las variables que tienen asignado ese objeto serán "apuntando" al mismo objeto original. Esto se debe a que una variable Cache no almacena el objeto en sí, sino solo la ubicación de un objeto Cache en la memoria.

Contraste esto con lo que sucede si declara un tipo struct en lugar de class. Ahora cuando declara una variable de ese tipo, la variable en sí tiene que ser lo suficientemente grande como para almacenar todos los datos declarados en el struct. Cada variable es una copia separada. Cada parámetro es una copia separada.

Puede anular esto agregando la palabra clave ref, pero es una palabra clave bastante inusual en la mayoría de los programas. La palabra clave out es más común, y se considera mejor como una forma de dar a un método más de un valor de retorno.

¿Qué efecto tiene ref en una variable si es de tipo class? En su ejemplo:

public ObjectLoader(Cache cache) { 
    // do stuff with cache (store it?) 
} 

que podría construir dos cargadores objeto de esta manera:

Cache c = new Cache(); 
ObjectLoader a = new ObjectLoader(c), 
ObjectLoader b = new ObjectLoader(c); 

¿Cuántos objetos qué acabamos de crear? Simplemente cuente las palabras clave new. Ahora, supongamos que agregamos la palabra clave ref:

public ObjectLoader(ref Cache cache) { 

    _cache = cache; // store   

    // do something very odd! 
    cache = new Cache(); 
} 

escondido dentro de ese constructor, he creado otro caché, y se almacena en el parámetro me pasaron. ¡Debido a que es un parámetro ref, he afectado la variable de la persona que llama! Así que en el código de llamada:

Cache c = new Cache(); 
ObjectLoader a = new ObjectLoader(ref c), 
ObjectLoader b = new ObjectLoader(ref c); 

Ahora tenemos cinco usos de new: tres en el fragmento anterior, además de dos llamadas a la ObjectLoader constructor modificado. Cada vez que se llama al constructor ObjectLoader, lo pasamos c. Tenemos que poner la palabra clave ref, que es algo muy bueno porque le permite a la persona que lee el código saber que algo extraño está sucediendo.La variable c apunta a un Cache diferente después de que el constructor ObjectLoader retorna. Así que b 's ObjectLoader termina almacenando un puntero a un Cache diferente a a!

No hace falta decir que este sería un patrón bastante complicado para el código. ¡Sería aún peor si no tuviéramos que poner la palabra clave ref en el sitio que llama!