2012-01-03 22 views
142

En C#, siempre he pensado que las variables no primitivas se pasaron por referencia y los valores primitivos pasaron por valor.Pasar objetos por referencia o valor en C#

De modo que al pasar a un método cualquier objeto no primitivo, cualquier cosa hecha al objeto en el método afectaría al objeto que se pasa. (C# 101 cosas)

Sin embargo, me he dado cuenta de que cuando paso un objeto System.Drawing.Image, que esto no parece ser el caso? Si paso un objeto system.drawing.image a otro método y cargo una imagen en ese objeto, entonces, ¿ese método sale del alcance y vuelvo al método de llamada, esa imagen no está cargada en el objeto original?

¿Por qué es esto?

+12

Todas las variables se pasan por valor de forma predeterminada en C#. pasando * el valor de la referencia * en el caso de los tipos de referencia. –

+0

Lo mismo: [why-is-list-when-passed-without-ref-to-a-function-acting-like-passed-with-ref] (http://stackoverflow.com/questions/7321602/why-is-list-when-passed-without-ref-to-a-function-acting-like-passed-with-ref) – nawfal

Respuesta

315

objetos no se pasan en absoluto. De forma predeterminada, el argumento se evalúa y su valor se pasa, por valor, como el valor inicial del parámetro del método que está llamando. Ahora, el punto importante es que el valor es una referencia para los tipos de referencia, una forma de llegar a un objeto (o nulo). Los cambios en ese objeto serán visibles desde la persona que llama. Sin embargo, al cambiar el valor del parámetro para hacer referencia a un objeto diferente, se mostrará no cuando esté usando pasar por valor, que es el valor predeterminado para todos los tipos.

Si desea utilizar el paso por referencia, se debe utiliza out o ref, si el tipo de parámetro es un tipo de valor o de un tipo de referencia. En ese caso, efectivamente la variable en sí misma se pasa por referencia, por lo que el parámetro usa la misma ubicación de almacenamiento que el argumento, y la persona que llama ve los cambios al parámetro en sí.

Así:

public void Foo(Image image) 
{ 
    // This change won't be seen by the caller: it's changing the value 
    // of the parameter. 
    image = Image.FromStream(...); 
} 

public void Foo(ref Image image) 
{ 
    // This change *will* be seen by the caller: it's changing the value 
    // of the parameter, but we're using pass by reference 
    image = Image.FromStream(...); 
} 

public void Foo(Image image) 
{ 
    // This change *will* be seen by the caller: it's changing the data 
    // within the object that the parameter value refers to. 
    image.RotateFlip(...); 
} 

que tienen un article which goes into a lot more detail in this. Básicamente, "pasar por referencia" no significa lo que piensas que significa.

+1

Tienes razón, no vi eso! Estoy cargando image = Image.FromFile (..) y eso estaba reemplazando la imagen variable y no cambiando el objeto! :) por supuesto. – michael

+0

en palabras simples ¿puedo decir si cambiamos las propiedades o llamamos a alguna función del parámetro? objeto que se verá afectado, pero si iniciamos el p la variable de arameter entonces será una referencia a una nueva ubicación/objeto. paramX.Caption = "asdasdasd"; // funcionará paramX = new Object(); // lo desconectará de la persona que llama y ese paramX se referirá a la nueva ubicación – Adeem

+1

@Adeem: No del todo - no hay un "objeto de parámetro", ahí está el objeto al que se refiere el valor del parámetro. Creo que tiene la idea correcta, pero la terminología importa :) –

2

Cuando pasa el objeto tipo System.Drawing.Image a un método, en realidad está pasando una copia de referencia a ese objeto.

Por lo tanto, si dentro de ese método está cargando una nueva imagen, la está cargando utilizando una referencia nueva/copiada.Así que usted no está haciendo el cambio en orignal

muestra
YourMethod(System.Drawing.Image image) 
{ 
    //now this image is a new reference 
    //if you load a new image 
    image = new Image().. 
    //you are not changing the original reference you are just changing the copy of original reference 
} 
11

Uno más código:

void Main() 
{ 


    int k = 0; 
    TestPlain(k); 
    Console.WriteLine("TestPlain:" + k); 

    TestRef(ref k); 
    Console.WriteLine("TestRef:" + k); 

    string t = "test"; 

    TestObjPlain(t); 
    Console.WriteLine("TestObjPlain:" +t); 

    TestObjRef(ref t); 
    Console.WriteLine("TestObjRef" + t); 
} 

public static void TestRef(ref int i) 
{ 
    i = 5; 
} 

public static void TestPlain(int i) 
{ 
    i = 5; 
} 

public static void TestObjRef(ref string s) 
{ 
    s = "TestObjRef"; 
} 

public static void TestObjPlain(string s) 
{ 
    s = "TestObjPlain"; 
} 

Y la salida:

TestPlain: 0

testref: 5

TestObjPlain: test

TestObjRefTestObjRef

0

En el paso por referencia se agrega sólo "ref" en los parámetros de la función y uno cosa más que debería estar la función "estática" debido declarando principal es estática (# public void main(String[] args))!

namespace preparation 
{ 
    public class Program 
    { 
     public static void swap(ref int lhs,ref int rhs) 
     { 
      int temp = lhs; 
      lhs = rhs; 
      rhs = temp; 
     } 
      static void Main(string[] args) 
     { 
      int a = 10; 
      int b = 80; 

    Console.WriteLine("a is before sort " + a); 
      Console.WriteLine("b is before sort " + b); 
      swap(ref a, ref b); 
      Console.WriteLine(""); 
      Console.WriteLine("a is after sort " + a); 
      Console.WriteLine("b is after sort " + b); 
     } 
    } 
} 
3

Supongo que es más claro cuando lo haces así. Recomiendo descargar LinkPad para probar cosas como esta.

void Main() 
{ 
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"}; 

    //Will update egli 
    WontUpdate(Person); 
    Console.WriteLine("WontUpdate"); 
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); 

    UpdateImplicitly(Person); 
    Console.WriteLine("UpdateImplicitly"); 
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); 

    UpdateExplicitly(ref Person); 
    Console.WriteLine("UpdateExplicitly"); 
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n"); 
} 

//Class to test 
public class Person{ 
    public string FirstName {get; set;} 
    public string LastName {get; set;} 

    public string printName(){ 
     return $"First name: {FirstName} Last name:{LastName}"; 
    } 
} 

public static void WontUpdate(Person p) 
{ 
    //New instance does jack... 
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName}; 
    newP.FirstName = "Favio"; 
    newP.LastName = "Becerra"; 
} 

public static void UpdateImplicitly(Person p) 
{ 
    //Passing by reference implicitly 
    p.FirstName = "Favio"; 
    p.LastName = "Becerra"; 
} 

public static void UpdateExplicitly(ref Person p) 
{ 
    //Again passing by reference explicitly (reduntant) 
    p.FirstName = "Favio"; 
    p.LastName = "Becerra"; 
} 

Y esa debe ser la salida

WontUpdate

Nombre: Egli, Apellido: Becerra

UpdateImplicitly

Nombre: Favio, Última nombre : Becerra

UpdateExplicitly

Nombre: Favio, Apellido: Becerra

0

La respuesta aceptada suena un poco mal y confuso. ¿Qué es una "copia de una referencia"?

¿Cómo la siguiente declaración sentido ?:

"Sin embargo, cambiar el valor del parámetro para referirse a un objeto diferente no será visible cuando se está usando el paso por valor, que es el valor predeterminado para todos los tipos." Pasar por valor no es el valor predeterminado para todos los tipos.

Su ejemplo en su enlace intenta establecer una instancia de un objeto a nulo. El objeto no se estableció correctamente en nulo debido a la recolección automática de basura. No se puede eliminar de esta manera.

Aquí hay un artículo de Microsoft que compara Java y C#.

De https://msdn.microsoft.com/en-us/library/ms836794.aspx

"todos los objetos son Referencias

Tipos de referencia son muy similares a los punteros en C++, en particular cuando el establecimiento de un identificador a alguna nueva instancia de clase.Pero al acceder a las propiedades o métodos de este tipo de referencia, use el "." operador, que es similar al acceso a las instancias de datos en C++ que se crean en la pila. Todas las instancias de clase se crean en el montón utilizando el nuevo operador, pero no se permite eliminar, ya que ambos lenguajes usan sus propios esquemas de recolección de basura, que se describen a continuación. "

Cuestiones relacionadas