Pregunta

Estoy creando una función donde necesito pasar un objeto para que la función pueda modificarlo.Cuál es la diferencia entre:

public void myFunction(ref MyClass someClass)

y

public void myFunction(out MyClass someClass)

¿Cuál debo usar y por qué?

¿Fue útil?

Solución

ref le dice al compilador que el objeto se inicializa antes de entrar en la función, mientras que out le dice al compilador que el objeto se iniciará dentro de la función.

Así, mientras ref es de dos maneras, es out fuera solamente.

Otros consejos

El ref modificador significa que:

  1. El valor ya está establecido y
  2. El método puede leerlo y modificarlo.

El out modificador significa que:

  1. El valor no está establecido y el método no puede leerlo. hasta esta organizado.
  2. El método debe configúrelo antes de regresar.

Digamos que Dom se presenta en el cubículo de Pedro acerca de la nota sobre los informes de la TPS.

Si Dom eran un argumento ref, habría una copia impresa de la nota.

Si Dom eran un argumento a cabo, que haría Peter imprimir una nueva copia del memorando para que él tome con él.

Voy a probar mi mano en una explicación:

Creo que entendemos cómo funcionan los tipos de valor correcto? Los tipos de valor son (int, long, estructura, etc.). Cuando se le envía a una función sin un comando ref copia los datos . Cualquier cosa que haga a esos datos en la función sólo afecta a la copia, no el original. El comando ref envía los datos reales y cualquier cambio afectará a los datos fuera de la función.

Ok a la parte confuso, tipos de referencia:

Vamos a crear un tipo de referencia:

List<string> someobject = new List<string>()

Cuando nuevos hasta someObject , se crean dos partes:

  1. El bloque de memoria que contiene los datos para someObject .
  2. Una referencia (puntero) para ese bloque de datos.

Ahora cuando se envía en someObject en un método sin ref copia los referencia puntero, no los datos. Por lo que ahora tiene este aspecto:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

Dos referencias que apuntan al mismo objeto. Si modifica una propiedad en someObject usando referencia2 que afectará a los mismos datos apuntado por Referencia1.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Si fuera nula referencia 2 o el punto de que los nuevos datos que no afectará ni la Referencia1 puntos Referencia1 datos.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Ahora lo que sucede cuando se envía someObject por ref a un método? La referencia real a someObject es enviado al método. Por lo que ahora sólo tiene una referencia a los datos:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

Pero ¿qué significa esto? Actúa exactamente lo mismo que enviar someObject no por ref excepto por dos cosas principales:

1) Cuando nula la referencia dentro del método que va a anular el uno fuera del método.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) Ahora puede señalar la referencia a una ubicación de datos completamente diferente y la referencia fuera de la función ahora se apunte a la nueva ubicación de los datos.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

ref es en y a cabo .

Debe utilizar out con preferencia dondequiera que es suficiente para sus necesidades.

Salida:

En C #, un método puede devolver un solo valor. Si te gusta a devolver más de un valor, puede utilizar la palabra clave a cabo. El retorno modificador como retorno por referencia. La respuesta más simple es que la palabra clave “fuera” se utiliza para obtener el valor del método.

  1. No es necesario inicializar el valor de la función de llamada.
  2. Se debe asignar el valor de la función llamada, de lo contrario el compilador informará de un error.

Ref:

En C #, cuando se pasa un tipo de valor tales como int, float, double etc. como un argumento para el parámetro del método, se pasa por valor. Por lo tanto, si modifica el valor del parámetro, que no afecta argumento en la llamada al método. Pero si se marca el parámetro con “ref” palabra clave, que se reflejará en la variable real.

  1. Es necesario para inicializar la variable antes de llamar a la función.
  2. No es obligatorio asignar ningún valor al parámetro de referencia en el método. Si no cambia el valor, ¿cuál es la necesidad de marcarlo como “ref”?

Extendiendo el ejemplo Dog, Cat. El segundo método con ref cambia el objeto referenciado por la persona que llama. Por lo tanto, "gato" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Desde que está pasando en un tipo de referencia (una clase) no hay necesidad de uso ref porque se pasa por defecto sólo una referencia para el objeto real y por lo tanto siempre se cambia el objeto detrás de la de referencia.

Ejemplo:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Mientras se pasa en una clase que no tiene que utilizar ref si desea cambiar el objeto dentro de su método.

ref y out comportan de manera similar, excepto siguientes diferencias.

  • variables ref debe ser inicializado antes de su uso. variable de out se puede utilizar sin asignación
  • parámetro out debe ser tratado como un valor no asignado por la función que lo utiliza. Por lo tanto, podemos utilizar el parámetro out inicializado en el código de llamada, pero el valor se perderán cuando la función se ejecuta.

Para los que aprenden con el ejemplo (como yo) esto es lo que Anthony Kolesov está diciendo .

He creado algunos ejemplos mínimos de referencia, hacia fuera, y otros para ilustrar el punto. No estoy cubriendo las mejores prácticas, a sólo ejemplos para comprender las diferencias.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

"panadero"

Eso es porque el primero cambia la cadena de referencia para indicar "panadero". Cambio de la referencia es posible debido a que ha pasado a través de ella la palabra clave ref (=> una referencia a una referencia a una cadena). La segunda llamada recibe una copia de la referencia a la cadena.

string ve algún tipo de especial en un principio. Pero cadena es sólo una clase de referencia y si define

string s = "Able";

entonces s es una referencia a una clase de cadena que contiene el texto "Capaz"! Otra asignación a la misma variable a través de

s = "Baker";

no cambia la cadena original, pero sólo crea una nueva instancia y dejar que el punto s a esa instancia!

Puede intentarlo con el siguiente ejemplo de código pequeño:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

¿Qué esperas? Lo que se obtiene es todavía "Capaz", ya que acaba de establecer la referencia en s a otra instancia mientras que los puntos S2 para la instancia original.

EDITAR: cadena también es inmutable que significa que simplemente no hay método o propiedad que modifica una instancia cadena existente (se puede tratar de encontrar una en la documentación pero no cualquier aletas :-)). Todos los métodos de manipulación de cadenas devuelven una instancia nueva cadena! (Es por eso que a menudo se tiene un mejor rendimiento al utilizar la clase StringBuilder)

ref significa que el valor en el parámetro ref ya está establecido, el método puede leer y modificar la misma. El uso de la palabra clave ref es lo mismo que decir que la persona que llama es responsable de la inicialización el valor del parámetro.


a cabo le dice al compilador que la inicialización del objeto es la responsabilidad de la función, la función tiene que asignar al parámetro de salida. No está permitido a dejarlo sin asignar.

Salida: Una instrucción de retorno se puede utilizar para devolver un único valor de una función. Sin embargo, el uso de los parámetros de salida, puede devolver dos valores de una función. Los parámetros de salida son como parámetros de referencia, excepto que la transferencia de datos fuera del método en lugar de en ella.

El siguiente ejemplo ilustra esto:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref: Un parámetro de referencia es una referencia a una ubicación de memoria de una variable. Cuando se pasa parámetros por referencia, a diferencia de los parámetros de valor, una nueva ubicación de almacenamiento no se crea para estos parámetros. Los parámetros de referencia representan la misma posición de memoria que los parámetros reales que se suministran con el método.

En C #, se declaran los parámetros de referencia utilizando la palabra clave ref. El siguiente ejemplo demuestra esto:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

ref y fuera funcionan igual que pasa por las referencias y pasando por punteros como en C ++.

Para ref, el argumento debe declarar e inicializar.

Por fuera, el argumento debe declarados, pero puede o no puede ser inicializado

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

Autoría Tiempo:

(1) Creamos el método de llamada Main()

(2) Se crea un objeto de lista (que es un objeto de tipo de referencia) y la almacena en la myList variable.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Durante Duración:

(3) Tiempo de ejecución asigna una memoria en la pila en el # 00, lo suficientemente amplia como para almacenar una dirección (# 00 = myList, ya que los nombres de variables son en realidad un alias para una localizaciones de memoria)

(4) Tiempo de ejecución crea un objeto de lista en el montón en la ubicación de memoria # FF (todas estas direcciones son, por ejemplo sakes)

(5) Tiempo de ejecución sería entonces almacenar el # FF dirección inicial del objeto en el # 00 (o en palabras, almacena la referencia del objeto de lista en el myList puntero)

Volver a la autoría Tiempo:

(6) A continuación, pasar el objeto de lista como argumento myParamList a la llamada modifyMyList método y asignar un nuevo objeto de lista a ella

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Durante Duración:

(7) Tiempo de ejecución comienza la rutina de llamada para el método llamado y como parte de ella, comprueba el tipo de parámetros.

(8) Al encontrar el tipo de referencia, se asigna una memoria en la pila en el # 04 para un alias a la variable de parámetro myParamList.

(9) A continuación, almacena el valor # FF en él también.

(10) Tiempo de ejecución crea un objeto de lista en el montón en la ubicación de memoria # 004 # FF y reemplaza en el # 04 con este valor (o sin referencia que el objeto de lista original y señaló que el nuevo objeto de lista en este método)

La dirección en el # 00 no se altera y conserva la referencia a # FF (o el puntero myList original no se altera).


La ref palabra clave es una directiva del compilador para omitir la generación de código de tiempo de ejecución para (8) y (9) lo que significa que no habrá asignación de montón para parámetros del método . Se utilizará el original puntero # 00 para operar en el objeto al # FF. Si el puntero del original no se inicializa, el tiempo de ejecución pondrá fin a quejarse que no puede proceder ya que la variable no se inicializa

La a cabo de palabras clave es una directiva de compilación que prácticamente es la misma que ref con una ligera modificación al (9) y (10). El compilador espera que el argumento de que se no inicializados y continuará con (8), (4) y (5) para crear un objeto en el montón y a las tiendas de su dirección de inicio en la variable argumento. será lanzado ningún error no inicializado y se perderá ninguna referencia previa almacenada.

Son más o menos la misma - la única diferencia es que una variable se pasa como un parámetro de salida no necesita ser inicializado, y el método que utiliza el parámetro de referencia tiene que ajustar a algo

.
int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

parámetros Ref son para datos que podrían ser modificados, fuera parámetros son para datos que es una salida adicional para la función (por ejemplo int.TryParse) que ya están usando el valor de retorno para algo.

A continuación he mostrado un ejemplo utilizando tanto Ref y fuera . Ahora, todo lo que se borrará por la referencia y por fuera.

En el ejemplo a continuación se menciona cuando comento // myRefObj = new MyClass {Name = "ref fuera llamada !!"}; línea, obtendrá un error que dice "El uso de variable local no asignada 'myRefObj'" , pero no hay tal error en a cabo .

¿Dónde utilizar Ref : cuando estamos llamando a un procedimiento con un parámetro y en el mismo parámetro se utilizará para almacenar la salida de ese proc

.

¿Dónde utilizar de salida: cuando estamos llamando a un procedimiento sin parámetros y en teh mismo parámetro se utiliza para devolver el valor de ese proc. También tenga en cuenta la salida

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

puede comprobar el código que le describa su differnce completa cuando se utiliza "ref" su media que u inicializar ya que int / cadena

pero  cuando se utiliza "fuera" funciona tanto en condiciones de tiempo lo que inicializar u int / cuerda o no pero u debe inicializar que int / cadena en esa función

Ref: La palabra clave ref se utiliza para pasar un argumento como referencia. Esto significa que cuando se cambia el valor de ese parámetro en el método, que se refleja en el método de llamada. Un argumento que se pasa con clave ref debe ser inicializado en el método de llamada antes de que se pasa al método llamado.

salida: La palabra clave out también se utiliza para pasar un argumento como palabra clave ref, pero el argumento se puede pasar sin asignar ningún valor a la misma. Un argumento que se pasa utilizando una palabra clave out debe ser inicializado en el método llamado antes de que vuelva de nuevo a la llamada al método.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

Ref y hacia fuera en la sobrecarga de métodos

Tanto ref y fuera no se pueden utilizar en la sobrecarga de métodos simultáneamente. Sin embargo, ref y hacia fuera son tratados de manera diferente en tiempo de ejecución pero son tratados mismo en tiempo de compilación (CLR no distingue entre los dos mientras se creó IL para ref y por fuera).

Desde el punto de vista de un método que recibe un parámetro, la diferencia entre ref y out es que C # requiere que los métodos deben escribir en todos los parámetros out antes de regresar, y no debe hacer nada con un parámetro de este tipo, aparte de pasarlo como un parámetro out o escribir en él, hasta que haya sido ya sea pasado como un parámetro de out a otro método o escrita directamente. Tenga en cuenta que algunos otros idiomas no imponen tales requisitos; un método virtual o interfaz que se declara en C # con un parámetro out puede ser anulado en otro idioma que no impone ninguna restricción especial en tales parámetros.

Desde el punto de vista de la persona que llama, C # voluntad en muchas circunstancias asumen cuando se llama a un método con un parámetro out hará que la variable pasada a ser escrito sin haber sido leído por primera vez. Esta suposición puede no ser correcta cuando se llama a métodos escritos en otros idiomas. Por ejemplo:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Si myDictionary identifica una implementación IDictionary<TKey,TValue> escrito en un idioma que no sea C #, aunque MyStruct s = new MyStruct(myDictionary); se parece a una misión, que potencialmente podría dejar s sin modificar.

Tenga en cuenta que los constructores escritos en VB.NET, a diferencia de los de C #, no hacen suposiciones acerca de si los llamados métodos modificarán los parámetros out, y claro todos los campos incondicionalmente. El extraño comportamiento aludido anteriormente no ocurrirá con el código escrito en su totalidad en VB o enteramente en C #, pero puede ocurrir cuando el código escrito en C # llama a un método escrito en VB.NET.

Si desea pasar su parámetro como una referencia a continuación, debe inicializarlo antes de pasar al parámetro de la función en sí compilador más va a mostrar la error.But en caso de parámetro de salida que no es necesario inicializar el parámetro objeto antes de pasar a la method.You puede inicializar el objeto en el método de llamada en sí.

Además de lo que le permite reasignar de otra persona variable a una instancia diferente de una clase, devolver múltiples valores, etc, utilizando ref o out deja a alguien más sabe lo que necesita de ellos y lo que va a hacer con el variables que proporcionan

  • no necesitan ref o out si todo lo que vamos a hacer es modificar las cosas dentro de MyClass la instancia que se pasa en el argumento someClass .

    • El método llamando verá cambios como someClass.Message = "Hello World" si se utiliza ref, out o nada
    • Escritura someClass = new MyClass() dentro swaps myFunction(someClass) a cabo el objeto visto por el someClass en el alcance del método myFunction solamente. El método de llamada todavía sabe acerca de la instancia MyClass originales se crea y se pasó a su método
  • necesidad ref o out si piensa en el intercambio de someClass a cabo para un objeto completamente nuevo y desea que el método de llamada para ver su cambio

    • Escribir someClass = new MyClass() dentro myFunction(out someClass) cambia el objeto visto por el método que llama myFunction
Existen

Otros programadores

Y quieren saber qué va a hacer con sus datos. Imagínese que usted está escribiendo una biblioteca que será utilizado por millones de desarrolladores. Usted quiere que ellos sepan lo que vas a hacer con sus variables cuando ellos llaman sus métodos

  • El uso de ref hace que una declaración de "Pasar una variable asignada a algún valor cuando se llama a mi método. Sé consciente de que podría cambiarlo por algo completamente distinto durante el curso de mi método. No hay que esperar la variable estar apuntando a la antigua objeto cuando he terminado "

  • El uso de out hace una declaración de "pasar una variable de marcador de posición a mi método No importa si tiene un valor o no;. El compilador me va a obligar a asignarlo a un nuevo valor absolutamente garantía. que el objeto apuntado por la variable antes de que llamó mi método, será diferente para el momento en que he terminado

Por cierto, en C # 7.2 hay un modificador in también

Y que impide el método de intercambio de la aprobada en instancia para una instancia diferente. Piense en ello como decir a esos millones de desarrolladores "pásame su referencia variable original, y prometo no intercambie sus datos cuidadosamente elaboradas a cabo para otra cosa". in tiene algunas particularidades, y en algunos casos, como cuando puede ser necesaria una conversión implícita para hacer su corta compatible con un in int el compilador temporalmente hacer un int, ampliar su corta para él, pasarlo por referencia y terminar. Se puede hacer esto porque se ha declarado que no va a meterse con él.


Microsoft hizo esto con los métodos .TryParse sobre los tipos numéricos:

int i = 98234957;
bool success = int.TryParse("123", out i);

Por marcar el parámetro como out que están declarando activamente aquí "estamos definitivamente va a cambiar su valor trabajado a conciencia de 98.234.957 a cabo para otra cosa"

Por supuesto, poco tienen que, para cosas como el análisis de los tipos de valor, porque si el método de análisis no se le permitió intercambiar el tipo de valor para otra cosa que no funcionaría muy bien .. Pero imaginan había algo de ficticio método en alguna biblioteca que está creando:

public void PoorlyNamedMethod(out SomeClass x)

Se puede ver que es un out, y por lo tanto se puede saber que si te pasas horas números de crujido, creando la perfecta SomeClass:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Está bien que era una pérdida de tiempo, teniendo todas esas horas para hacer que la clase perfecta. Definitivamente va a ser lanzado de distancia y replaced por PoorlyNamedMethod

Mind bien que el parámetro de referencia que se hace pasar dentro de la función se trabaja directamente sobre.

Por ejemplo,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Esto escribirá el perro, el gato no. Por lo tanto debe trabajar directamente en someObject.

Puede que no sea tan bueno en esto, pero seguramente cuerdas (a pesar de que son técnicamente referencia a los tipos y vivir en el montón) se pasan por valor, no referencia?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

Esto por qué necesita ref si se quiere que los cambios existen fuera del ámbito de la función de hacer ellos, no están pasando una referencia de otro modo.

Por lo que yo soy consciente de que sólo necesita para ref structs tipos de valor y / secuencia sí mismo, como cadena es un tipo de referencia que pretende que es, pero no es un tipo de valor.

Podría estar totalmente equivocado aquí, sin embargo, soy nuevo.

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