2010-12-03 16 views
44

Sigo recibiendo el mismo problema una y otra vez donde se copia un objeto que quiero referencia o cuando se hace referencia a un objeto que quiero copiar. Esto sucede cuando uso el operador =.¿Cuándo se copia un valor/objeto C# y cuándo se copia su referencia?

Por ejemplo, si yo voy a enviar el objeto a otra forma, es decir:

SomeForm myForm = new SomeForm(); 
SomeObject myObject = new SomeObject(); 
myForm.formObject = myObject; 

... y luego modificar el objeto en la forma, el objeto original no se modifican. Es como si el objeto se hubiera copiado y no se hubiera hecho referencia a él. Sin embargo, cuando hago esto:

SomeObject myObject = new SomeObject(); 
SomeObject anotherObject = new SomeObject(); 
anotherObject = myObject; 

... y luego modificar anotherObject, myObject es modificado también.

El caso más agravante es cuando intento clonar uno de mis objetos definidos:

public class SomeObject 
{ 
    double value1, value2; 

    //default constructor here 

    public SomeObject(val1, val2) 
    { 
     value1 = val1; 
     value2 = val2; 
    } 

    public void Clone(SomeObject thingToCopy) 
    { 
     this.value1 = thingToCopy.value1; 
     this.value2 = thingToCopy.value2; 
    } 
} 

cuando hago esto ...

SomeObject obj1 = new SomeObject(1, 2); 
SomeObject obj2 = new SomeObject(); 
obj2.Clone(obj1); 

... obj1 se referencia y cualquier modificación a obj2 cambia obj1.

Objetos del sistema como int, double, string, etc. parecen copiarse siempre, excepto en el caso del método de clonación anterior.

Mi pregunta es, no teniendo en cuenta el uso de la palabra clave ref en funciones, cuándo se copia un objeto y cuándo se hace referencia a un objeto en cada caso (es decir, al pasar a funciones, al establecer como otros objetos (como los primeros dos ejemplos anteriores), al copiar variables de miembros como el tercer ejemplo, etc.)?

Respuesta

32

Es difícil responder a este tipo de preguntas de forma precisa sin perder mucho tiempo eligiendo cuidadosamente sus palabras.

que he hecho hasta en un par de artículos que pueden serle de utilidad:

Eso no quiere decir que los artículos son perfectos, por supuesto - lejos de eso, pero he intentado ser lo más claro posible.

Creo que una cosa importante es separar los dos conceptos (paso de parámetros y tipos de referencia frente a valor) en tu cabeza.

de mirar a sus ejemplos específicos:

SomeForm myForm = new SomeForm(); 
SomeObject myObject = new SomeObject(); 
myForm.formObject = myObject; 

Esto significa que myForm.formObject y myObject se refieren a la misma instancia de SomeObject - como dos personas que tienen hojas separadas de papel, con cada una de ellas con la misma dirección escrita en ellos. Si va a la dirección en una hoja de papel y pinta la casa de rojo, luego vaya a la dirección en la segunda hoja de papel, verá una casa roja.

No está claro lo que quiere decir con "y luego modifique el objeto en el formulario" porque el tipo que ha proporcionado es inmutable. No hay forma de modificar el objeto en sí. Puede cambiar myForm.formObject para referirse a una instancia diferente de SomeObject, pero eso es como garabatear la dirección en una hoja de papel y escribir en ella una dirección diferente. Eso no cambiará lo que está escrito en la otra hoja de papel.

Si pudiera proporcionar un programa completo completo cuyo comportamiento no comprenda (idealmente una aplicación de consola, simplemente para mantener las cosas más cortas y simples) sería más fácil hablar de cosas en términos concretos.

+0

Voy a tener que escribir uno . Comprendo punteros en su mayor parte de C++, simplemente no entiendo cuando se usan en relación con el valor y los tipos de referencia en C#, ya que no hay operadores/operadores de puntero "seguros" explícitos en C#. –

+1

Escribiré el programa más tarde ya que ahora estoy en el trabajo. Gracias por la ayuda. Leeré los artículos también. –

+3

La analogía de la dirección en hojas separadas de papel es útil para captar el concepto de tipos de referencia. – Martin

4

Hola Mike Todos los objetos, que se derivan de ValueType, como struct u otros tipos primitivos son tipos de valores. Eso significa que se copian siempre que los asigne a una variable o los pase como un parámetro de método. Otros tipos son tipos de referencia, lo que significa que, cuando asigna un tipo de referencia a una variable, no es su valor, sino que su dirección en el espacio de la memoria se asigna a la variable. También debe tener en cuenta que puede pasar un tipo de valor como referencia utilizando la palabra clave ref. Aquí está la sintaxis

public void MyMethod(ref int a) { a = 25 } 
int i = 20; 
MyMethod(ref i); //Now i get's updated to 25. 

espero que ayude :)

+0

Disculpe a nitpick aquí. Los objetos no se derivan Los tipos lo hacen Objeto es generalmente el término utilizado para describir una instancia de un tipo. –

+0

Puede pasar un argumento * por * referencia usando 'ref', pero eso no es lo mismo que pasarlo * como * una referencia. Creo que vale la pena diferenciar entre los dos. –

+0

@JonSkeet: Sé que esta es una vieja pregunta, pero ¿puedes aclarar o compartir información sobre tu último comentario? Me refiero a pasar _por_ referencia vs pasar _ como_ referencia? gracias de antemano :) Pregunto porque siempre pensé que ambos terminarían con el mismo resultado que afecta el valor del objeto pasado si se pasa dentro de un método externo. –

1

Con respecto a la clonación de sus objetos si los valores que va a copiar de un objeto a otro son los tipos de referencia, entonces cualquier modificación de estos valores en el objeto original se afectar los valores en el objeto copiado (ya que son solo referencias al mismo objeto)

Si necesita clonar un objeto que tiene propiedades que son tipos de referencia, necesita hacer esos tipos clonable o hacer una copia manual de ellos instalando instancias nuevas según sea necesario.

Considere el uso de la interfaz IClonable, aunque no es la mejor de las soluciones imho.

Cuestiones relacionadas