Pregunta

Tengo un objeto que está en el estado de memoria del programa y también tengo algunas otras funciones de trabajo a las que le paso el objeto para modificar el estado. Lo he estado pasando por referencia a las funciones de los trabajadores. Sin embargo, me encontré con la siguiente función.

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

Me confunde porque tanto recibido_s como remoteEP están devolviendo cosas de la función. ¿Por qué remoteEP necesita una ref y recibido_s no?

También soy un programador en C, por lo que tengo problemas para eliminar los punteros de mi cabeza.

Editar: Parece que los objetos en C # son punteros al objeto debajo del capó. Entonces, cuando pasa un objeto a una función, puede modificar el contenido del objeto a través del puntero y lo único que se pasa a la función es el puntero al objeto para que el objeto en sí no se copie. Utilice ref o out si desea poder cambiar o crear un nuevo objeto en la función que es como un puntero doble.

¿Fue útil?

Solución

Respuesta corta: lea mi artículo sobre la aprobación de argumentos .

Respuesta larga: cuando un parámetro de tipo de referencia se pasa por valor, solo se pasa la referencia, no una copia del objeto. Esto es como pasar un puntero (por valor) en C o C ++. La persona que llama no verá los cambios en el valor del parámetro en sí, pero sí los cambios en el objeto que las referencias a se verán .

Cuando un parámetro (de cualquier tipo) se pasa por referencia , eso significa que cualquier cambio en el parámetro es visto por la persona que llama; los cambios en el parámetro son cambios a la variable.

El artículo explica todo esto con más detalle, por supuesto :)

Respuesta útil: casi nunca necesita usar ref / out . Básicamente es una forma de obtener otro valor de retorno, y generalmente debe evitarse precisamente porque significa que el método probablemente esté tratando de hacer demasiado. Ese no es siempre el caso ( TryParse etc. son los ejemplos canónicos del uso razonable de out ) pero usar ref / out debería ser una rareza relativa.

Otros consejos

Piense en un parámetro no ref como un puntero y en un parámetro ref como un puntero doble. Esto me ayudó más.

Casi nunca debe pasar valores por ref. Sospecho que si no fuera por problemas de interoperabilidad, el equipo .Net nunca lo habría incluido en la especificación original. La forma OO de tratar la mayoría de los problemas que resuelven los parámetros de referencia es:

Para valores de retorno múltiples

  • Crear estructuras que representen los múltiples valores de retorno

Para primitivas que cambian en un método como resultado de la llamada al método (el método tiene efectos secundarios en los parámetros primitivos)

  • Implemente el método en un objeto como método de instancia y manipule el estado del objeto (no los parámetros) como parte de la llamada al método
  • Use la solución de valor de retorno múltiple y combine los valores de retorno a su estado
  • Cree un objeto que contenga un estado que pueda ser manipulado por un método y pase ese objeto como parámetro, y no las primitivas mismas.

Probablemente podría escribir una aplicación C # completa y nunca pasar ningún objeto / estructura por referencia.

Tuve un profesor que me dijo esto:

  

El único lugar donde usaría las referencias es donde:

     
      
  1. Quiere pasar un objeto grande (es decir, los objetos / estructura tiene   objetos / estructuras dentro de él a múltiples niveles) y copiarlo lo haría   ser costoso y
  2.   
  3. Está llamando a un Framework, API de Windows u otra API que requiere   
  4.   
     

No lo hagas solo porque puedes. Puedes ser mordido por algunos   errores desagradables si comienza a cambiar los valores en un parámetro y no   prestando atención.

Estoy de acuerdo con su consejo, y en mis más de cinco años desde la escuela, nunca lo he necesitado fuera de llamar al Framework o API de Windows.

Dado que recibido_s es una matriz, está pasando un puntero a esa matriz. La función manipula los datos existentes en su lugar, sin cambiar la ubicación subyacente o el puntero. La palabra clave ref significa que está pasando el puntero real a la ubicación y actualizando ese puntero en la función externa, por lo que el valor en la función externa cambiará.

Por ejemplo. la matriz de bytes es un puntero a la misma memoria antes y después, la memoria acaba de actualizarse.

La referencia de punto final está actualizando el puntero al punto final en la función externa a una nueva instancia generada dentro de la función.

Piense en una referencia como si estuviera pasando un puntero por referencia. No usar una referencia significa que está pasando un puntero por valor.

Mejor aún, ignore lo que acabo de decir (probablemente sea engañoso, especialmente con los tipos de valor) y lea Esta página de MSDN .

entiendo que todos los objetos derivados de la clase Object se pasan como punteros, mientras que los tipos ordinarios (int, struct) no se pasan como punteros y requieren ref. No estoy seguro acerca de la cadena (¿se deriva en última instancia de la clase Object?)

Si bien estoy de acuerdo con la respuesta de Jon Skeet en general y algunas de las otras respuestas, existe un caso de uso para usar ref , y eso es para optimizar las optimizaciones de rendimiento. Se ha observado durante el perfil de rendimiento que establecer el valor de retorno de un método tiene ligeras implicaciones de rendimiento, mientras que el uso de ref como argumento mediante el cual el valor de retorno se rellena en ese parámetro da como resultado que se elimine este pequeño cuello de botella.

Esto es realmente útil solo cuando los esfuerzos de optimización se llevan a niveles extremos, sacrificando la legibilidad y quizás la capacidad de prueba y mantenimiento para ahorrar milisegundos o tal vez milisegundos divididos.

Regla de la zona cero primero, las primitivas se pasan por valor (pila) y las no primitivas por referencia (montón) en el contexto de los TIPOS involucrados.

Los parámetros involucrados se pasan por valor de forma predeterminada. Buena publicación que explica las cosas en detalle. http://yoda.arachsys.com/csharp/parameters.html

Student myStudent = new Student {Name="A",RollNo=1};

ChangeName(myStudent);

static void ChangeName(Student s1)
{
  s1.Name = "Z"; // myStudent.Name will also change from A to Z
                // {AS s1 and myStudent both refers to same Heap(Memory)
                //Student being the non-Primitive type
}

ChangeNameVersion2(ref myStudent);
static void ChangeNameVersion2(ref Student s1)
{
  s1.Name = "Z"; // Not any difference {same as **ChangeName**}
}

static void ChangeNameVersion3(ref Student s1)
{
    s1 = new Student{Name="Champ"};

    // reference(myStudent) will also point toward this new Object having new memory
    // previous mystudent memory will be released as it is not pointed by any object
}

Podemos decir (con advertencia) Los tipos no primitivos no son más que Punteros Y cuando los pasamos por ref podemos decir que estamos pasando Double Pointer

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top