Pasar objetos por referencia o valor en C #

En C #, siempre he pensado que las variables no primitivas se pasaban por referencia y los valores primitivos pasaban por valor.

Por lo tanto, 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, ¿no parece ser así? Si paso un objeto system.drawing.image a otro método y cargo una imagen en ese objeto, entonces, ¿ese método sale del scope y vuelvo al método de llamada, esa imagen no está cargada en el objeto original?

¿Por qué es esto?

Los objetos no se pasan en absoluto. Por defecto, 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, cambiar el valor del parámetro para hacer referencia a un objeto diferente no estará visible cuando utilice el valor de paso por paso, que es el valor predeterminado para todos los tipos.

Si desea utilizar pass-by-reference, debe usar out o ref , si el tipo de parámetro es un tipo de valor o 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í.

Asi que:

 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(...); } 

Tengo un artículo que entra en muchos más detalles al respecto . Básicamente, “pasar por referencia” no significa lo que piensas que significa.

Una muestra de código más para mostrar esto:

 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 el resultado:

TestPlain: 0

TestRef: 5

TestObjPlain: prueba

TestObjRef: TestObjRef

Creo 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 eso debería producir

WontUpdate

Nombre: Egli, Apellido: Becerra

UpdateImplicitly

Nombre: Favio, Apellido: Becerra

UpdateExplicitly

Nombre: Favio, Apellido: Becerra

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

Entonces, si dentro de ese método está cargando una nueva imagen, la está cargando usando una referencia nueva / copiada. No estás haciendo cambios en el original.

 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 } 

¿Cómo pasaste el objeto al método?

¿Estás haciendo algo nuevo dentro de ese método por objeto? Si es así, debes usar ref en el método.

El siguiente enlace te dará una mejor idea.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html

En Pass By Reference solo agrega “ref” en los parámetros de la función y una cosa más debe declarar la función “static” porque main es estático (# 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); } } } 

La respuesta aceptada suena un poco equivocada y confusa. ¿Qué es una “copia de una referencia”?

¿Cómo tiene sentido la siguiente statement ?:

“Sin embargo, cambiar el valor del parámetro para hacer referencia a un objeto diferente no será visible cuando use pass by value, 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 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 #.

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

“Todos los objetos son referencias

Los tipos de referencia son muy similares a los punteros en C ++, particularmente cuando se establece un identificador para una 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 stack. 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.