pass - ref out c# ejemplos
¿Cómo funciona la palabra clave ref(en términos de memoria) (4)
Pasando una variable local como referencia
En el nivel bajo, la variable int
local referenciada se colocará en la pila (la mayoría de los enteros de tiempo se almacenarán en registros), y se pasará un puntero a la pila a la función invocada (el puntero en sí es más probable que se pase) en un registro). Considere el siguiente ejemplo:
var i = 7;
Console.WriteLine(i);
inc(ref i);
Console.WriteLine(i);
Esto será JIT-et a algo como esto (la arquitectura de destino es x86):
17: var i = 7;
# allocate space on the stack for args and i
00482E3B sub esp,8
# initialize i to 0
00482E3E xor eax,eax
00482E40 mov dword ptr [ebp-8],eax
# args saved to stack (could be optimised out)
00482E43 mov dword ptr [ebp-4],ecx
00482E46 cmp dword ptr ds:[3ACAECh],0
00482E4D je 00482E54
00482E4F call 7399CB2D
# i = 7
00482E54 mov dword ptr [ebp-8],7
18: Console.WriteLine(i);
# load the value of i into ecx, and call cw
00482E5B mov ecx,dword ptr [ebp-8]
00482E5E call 72E729DC
19: inc(ref i);
# load the address of i into ecx, and call inc
00482E63 lea ecx,[ebp-8]
00482E66 call dword ptr ds:[4920860h]
20: Console.WriteLine(i);
# load the value of i into ecx, and call cw
00482E6C mov ecx,dword ptr [ebp-8]
00482E6F call 72E729DC
21: }
00482E74 nop
00482E75 mov esp,ebp
00482E77 pop ebp
00482E78 ret
Pasar un elemento de matriz o un miembro de objeto como referencia
Casi lo mismo sucede aquí, se obtiene la dirección del campo o elemento, y el puntero se pasa a la función:
var i = new[]{7};
Console.WriteLine(i[0]);
inc(ref i[0]);
Console.WriteLine(i[0]);
Compila (sin la parte aburrida):
18: Console.WriteLine(i[0]);
00C82E91 mov eax,dword ptr [ebp-8]
00C82E94 cmp dword ptr [eax+4],0
00C82E98 ja 00C82E9F
00C82E9A call 7399BDC2
00C82E9F mov ecx,dword ptr [eax+8]
00C82EA2 call 72E729DC
19: inc(ref i[0]);
# loading the reference of the array to eax
00C82EA7 mov eax,dword ptr [ebp-8]
# array boundary check is inlined
00C82EAA cmp dword ptr [eax+4],0
00C82EAE ja 00C82EB5
# this would throw an OutOfBoundsException, but skipped by ja
00C82EB0 call 7399BDC2
# load the address of the element in ecx, and call inc
00C82EB5 lea ecx,[eax+8]
00C82EB8 call dword ptr ds:[4F80860h]
Tenga en cuenta que la matriz no tiene que estar anclada en este caso , ya que el marco de trabajo sabe que la dirección en ecx
está apuntando un elemento dentro de la matriz, por lo que si se produce una compresión del montón entre lea
y call
o dentro de la función inc, puede reajustar el valor de ecx
directamente.
Puede investigar el ensamblaje JIT-ed usted mismo utilizando el depurador de Visual Studio abriendo la ventana Desensamblar ( Depurar / Windows / Desensamblar )
C # tiene una palabra clave ref . Usando ref puedes pasar un int a un método por referencia. ¿Qué sucede en el marco de la pila cuando llama a un método que acepta un int por referencia?
public void SampleMethod(ref int i) { }
Aquí hay un ejemplo simple en código C #:
void Main()
{
int i = 1;
inc(ref i);
Console.WriteLine(i);
}
public void inc(ref int i) {
i++;
}
Aquí está el código IL generado
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0 // i
IL_0003: ldarg.0
IL_0004: ldloca.s 00 // i
IL_0006: call inc
IL_000B: nop
IL_000C: ldloc.0 // i
IL_000D: call System.Console.WriteLine
IL_0012: nop
IL_0013: ret
inc:
IL_0000: nop
IL_0001: ldarg.1
IL_0002: dup
IL_0003: ldind.i4
IL_0004: ldc.i4.1
IL_0005: add
IL_0006: stind.i4
IL_0007: ret
Tenga en cuenta que con este caso simple realmente hay solo una diferencia: ldloca.s 00 o ldloc.0. Cargar dirección local o de carga (de offset 00)
Esa es la diferencia en el nivel más simple (que es lo que pidió en su comentario): si carga el valor de la variable o si carga la dirección de la variable. La cosa puede complicarse rápidamente: si la función que está llamando no es local, si la variable que está pasando no es local, etc., etc. Pero a un nivel básico, esta es la diferencia.
Usé el linqpad para hacer mi desmontaje rápido, lo recomiendo. http://www.linqpad.net/
La dirección de la variable o campo local. En la IL, la instrucción ldloca.s
se usa para una variable local.
Carga la dirección de la variable local en un índice específico en la pila de evaluación
La instrucción stind
se utiliza para almacenar el valor de nuevo en la variable
Almacene el valor del tipo (...) en la memoria en la dirección
La dirección es de 32/64 bits, dependiendo de la arquitectura de destino.
Pasará la variable local por referencia en lugar de enviar una nueva copia para ella