2010-02-03 11 views
9

He leído la diferencia entre passng y no pasar ref en los parámetros; sin embargo, ¿cuándo querría usarlos?Cuándo pasar la palabra clave ref en

Por ejemplo, tenía un poco de lógica en un método que podría refactorizarse en su propio método. Resharper 4.5 convirtió uno de los parámetros en un tipo de referencia, pero no pensé que lo hubiera hecho si lo hubiera hecho manualmente.

Obviamente me falta algo de comprensión. Quizás un ejemplo de lo que sucede cuando ciertos tipos o escenarios en la codificación pierden la palabra clave ref ayudará?

Gracias

+1

y cuándo pasar '' out'? – Natrium

+0

código postal por curiosidad? – bdd

+0

@Natrium: me parece que es el momento después de que retroceda unos pocos a muchos :) –

Respuesta

8

Let Me dividirla en dos preguntas:

1) ¿Cuándo se debe utilizar declaraciones de parámetros ref de entrada/salida formal cuando escribiendo un método?

Uso ref/cuando desea que su método sea capaz de leer y escribir una variable pasaron por la persona que llama, en lugar de una simple lectura de un valor .

2) ¿Por qué una refactorización de "método de extracción" produce un parámetro ref?

No conozco los detalles de Resharper, pero puedo adivinar.Considere el siguiente mal tipo de valor mutable:

struct S 
{ 
    private int x; 
    public int X() { return this.x; } 
    public void M() { this.x += 1; } 
} 

Usted tiene un método:

void Foo() 
{ 
    S s = new S(); 
    Fred(s); 
    Blah(s); 
    Bar(s); 
    s.M(); 
    Console.WriteLine(s.X()); // prints 1 
} 

y lo hace "método de extracto de" en el bit medio:

void NewMethod(ref S s) 
{ 
    Blah(s); 
    Bar(s); 
    s.M(); 
} 

void Foo() 
{ 
    S s = new S(); 
    Fred(s); 
    NewMethod(ref s); 
    Console.WriteLine(s.X()); // still prints 1 
} 

Si por el contrario usted hizo un método sin "ref" y luego llamar NewMethod (s) pasaría una copia de s a NewMethod. Recuerde, los tipos de valores se copian por valor; es por eso que los llamamos "tipos de valor". Sería la copia que se muta, y luego s.X() devuelve cero. Es una mala idea para una refactorización introducir un cambio semántico en un programa, y ​​es difícil para un motor de refactorización saber si un método dado se basa o no en la mutabilidad de un tipo de valor.

Esta es solo otra razón más por la que debe evitar los tipos de valores variables.

1

Pasando byref tiene sentido sólo para "efectos secundarios" de una función: es decir, que tienen la intención de modificar un parámetro de tipo de valor, o reasignar otro objeto a un parámetro de objeto dado, y tienen que el cambio sobrevivir la llamada a la función. Ejemplo: TryGetValue().

De lo contrario, mejor quedarse con byval.

+1

En realidad 'TryGetValue()' utiliza la palabra clave 'out' donde la persona que llama no es necesaria para inicializar la variable, pero el destinatario es. La palabra clave 'ref' se usa en los casos en que el destinatario * MAYO * puede cambiar la referencia del objeto. –

0

Conceptualmente, la diferencia es que un tipo de valor almacena su valor directamente, mientras que un tipo de referencia almacena una referencia al valor. Tal vez deberías volver a leer un poco acerca de los tipos de referencia vs. valor.

Pasar los tipos de valores por referencia, como se demostró anteriormente, es útil, pero la referencia también es útil para pasar los tipos de referencia. Esto permite que los métodos llamados modifiquen el objeto al que hace referencia la referencia porque la referencia en sí misma se pasa por referencia. El siguiente ejemplo muestra que cuando se pasa un tipo de referencia como un parámetro ref, el objeto en sí puede cambiarse.

class RefRefExample 
{ 
    static void Method(ref string s) 
    { 
     s = "changed"; 
    } 
    static void Main() 
    { 
     string str = "original"; 
     Method(ref str); 
     // str is now "changed" 
    } 
} 

MSDN

0

El efecto es que cualquier cambio en el parámetro en el método se reflejarán en esa variable cuando el control pasa de nuevo al método de llamada. Así que si desea que el valor asignado al parámetro para sobrevivir más allá del método de llamar es un posible caso de uso

0

Considere este ejemplo:

static int i = 3; 

public static void ChangeIntRef(ref int val) 
{ 
    val = 5; 
} 

public static void ChangeInt(int val) 
{ 
    val = 5; 
} 

Console.WriteLine(i); 
ChangeInt(i); 
Console.WriteLine(i); 

ChangeIntRef(ref i); 
Console.WriteLine(i); 

Al pasar el parámetro como ref, le está diciendo al compilador que lo que realmente quiere es una referencia a la variable original para ser pasado al método. Como resultado, el método puede cambiar el valor de la variable original.

Si ejecuta el fragmento de arriba, el resultado es:

3 
3 
5 

Esto debe mostrar claramente que sin la palabra clave ref, el método ChangeInt no es capaz de cambiar realmente el valor original. Sin embargo, con la palabra clave ref, el método ChangeIntRef puede cambiar el valor original.

0

Uso referencias para semántica. Considere este enfoque:

void AddResultsTable(ref PlaceHolder p) // modifies p; adding a table 
{ 
    var t = new Table(); 

    AddTableHeader(ref t); // modifies t; adding a table header 

    AddTableBody(ref t); // modifies t; adding a table body 

    AddTableFooter(ref t); // modifies t; adding a table footer 

    p.Controls.Add(t); 
} 

AddResultsTable(ref PlaceHolderResults); 

Versus éste:

Table ReturnTable() 
{ 
    var t new Table(); 

    // AddTableHeader() returns TableHeader 
    t.Columns.HeaderColumns.Add(ReturnTableHeader()); 

    // ... etc. 

    return t; 
} 

PlaceHolder.Controls.Add(ReturnTable()); 

El primer fragmento de código se ve más limpio para mí; métodos modifica objetos en lugar de devolver nuevos objetos que a su vez tiene que agregar. Todo queda "encajonado" y escondido, dentro de los métodos.

Cuestiones relacionadas