¿Cuál es la diferencia entre ref y out en tiempo de ejecución?


15

C # proporciona la refy la outpalabra clave para hacer que los argumentos se pasen por referencia. La semántica de los dos es muy similar. La única diferencia está en la inicialización de la variable con bridas:

  • refrequiere que la variable se inicialice antes de pasarla a la función, outno lo hace.
  • outrequiere que la variable se inicialice dentro de la función, refno.

Los casos de uso de estas dos palabras clave también son casi los mismos, y su uso demasiado frecuente es considerado un olor a código (aunque hay casos de uso válidos como el TryParsey los TryGetValuepatrones).

Debido a esto, alguien podría explicar, ¿por qué hay dos herramientas muy similares en C # para casos de uso tan limitados?

Además, en MSDN , se afirma que tienen un comportamiento de tiempo de ejecución diferente:

Aunque las palabras clave ref y out provocan un comportamiento de tiempo de ejecución diferente, no se consideran parte de la firma del método en tiempo de compilación.

¿Cómo es diferente su comportamiento en tiempo de ejecución?

Conclusión

Ambas respuestas parecen correctas, gracias a ambos. Acepté jmoreno 's porque es más explícito.


Supongo que ambos se implementan de la misma manera que refen C ++; a punteros al puntero de objeto (o primitivo) en cuestión. es decir, Int32.TryParse(myStr, out myInt)(C #) se "ejecuta" de la misma manera que lo int32_tryParse(myStr, &myInt)haría (C); La única diferencia son algunas restricciones impuestas por el compilador para evitar errores. (No voy a publicar esto como respuesta porque puedo estar equivocado acerca de cómo funciona esto detrás de escena, pero así es como imagino que funciona [porque tiene sentido])
Cole Johnson

Respuestas:


10

En el momento en que se hizo esta pregunta, el artículo de MSDN era sutilmente incorrecto ( desde entonces se ha corregido ). En lugar de "causar un comportamiento diferente" debería ser "requiere un comportamiento diferente".

En particular, el compilador impone diferentes requisitos para las dos palabras clave, a pesar de que se utiliza el mismo mecanismo (IL) para habilitar el comportamiento.


Entonces, entiendo que el compilador verifica cosas como desreferenciar un outparámetro o no asignar un refparámetro, pero la IL que emite es solo una referencia de cualquier manera. ¿Es eso correcto?
Andrew

Por cierto, uno podría decir esto por el hecho de que out Ty ref Tse representan de forma idéntica en otros lenguajes CLI como VB.Net o C ++ / CLI.
ach

@ AndrewPiliser: correcto. Suponiendo que el código se compila de cualquier manera (no intente desreferenciar cuando no debería), el IL sería idéntico.
jmoreno

4

Aquí hay una reseña interesante sobre el tema que puede responder a su pregunta:

http://www.dotnetperls.com/ref

punto de interés:

"La diferencia entre ref y out no está en Common Language Runtime, sino en el lenguaje C # mismo".

actualizar:

el desarrollador senior de mi trabajo simplemente corroboró la respuesta de @ jmoreno, ¡así que asegúrese de leerla!


Entonces, si entiendo correctamente, no hay diferencia en el nivel de IL, lo que implica que no pueden tener un comportamiento de tiempo de ejecución diferente. Esto estaría en contradicción con lo que dice MSDN. ¡Artículo interesante, sin embargo!
Gábor Angyal

@ GáborAngyal, por casualidad, no tienes un enlace al artículo de MSDN, ¿verdad? Sería bueno tener eso aquí si las opiniones difieren
Dan Beaulieu

Aquí tienes: msdn.microsoft.com/en-gb/library/t3c3bfhx.aspx , pero estuvo en mi pregunta todo el tiempo :)
Gábor Angyal

1
¿Qué stringtiene que ver con todo esto?
Robert Harvey

Es solo una paráfrasis del artículo. Actualizado para eliminar la segunda línea ..
Dan Beaulieu

2

Tiempo de ejecución ? Absolutamente ninguno. No puede sobrecargar un método con los mismos parámetros que difieren solo por palabra clave ref o out.

Intente compilar esto y obtendrá un error de compilación "El método con la misma firma ya está declarado":

    private class MyClass
    {
        private void DoSomething(out int param)
        {
        }
        private void DoSomething(ref int param)
        {
        }
    }

Para responder a esta pregunta: "... ¿por qué hay dos herramientas muy similares en C # para casos de uso tan limitados?"

Desde el punto de vista de la legibilidad del código y la API, hay una gran diferencia. Como consumidor de la API, sé que cuando se usa "fuera", la API no depende del parámetro de salida. Como desarrollador de API, prefiero "out" y uso "ref" solo cuando es absolutamente (¡RARAMENTE!) Necesario. Vea esta referencia para una gran discusión:

/programming/1516876/when-to-use-ref-vs-out

Información de soporte: compilé el siguiente método y lo desarme. Utilicé las palabras clave ref y out (en este ejemplo), pero el código de ensamblaje no cambió excepto por una referencia de dirección como era de esperar:

    private class MyClass
    {
        internal void DoSomething(out int param)
        {
            param = 0;
        }
    }

00000000 empuje ebp
00000001 mov ebp, esp
00000003 empuje edi
00000004 empuje esi
00000005 empuje ebx
00000006 sub esp, 34h
00000009 xor eax, eax
0000000b mov dword ptr [ebp-10h], eax
0000000e mov dword ptr [ebp-1Ch], EAX
00000011 mov dword ptr [ebp-3Ch], ecx
00000014 mov dword ptr [ebp-40h], edx
00000017 cmp dword ptr ds: [008B1710h], 0
0000001e je 00000025
00000020 llame al 6E6B601E
00000025 nop
param = 0;
00000026 mov eax, dword ptr [ebp-40h]
00000029 xor edx, edx
0000002b mov dword ptr [eax], edx
}
0000002d nop
0000002e lea esp, [ebp-0Ch]
00000031 pop ebx
00000032 pop esi
00000033 pop edi
00000034 pop ebp
00000035 ret

¿Estoy leyendo el ensamblaje correctamente?


Lo que escribe es correcto, pero tenga en cuenta que no es una implicación evidente de que no hay diferencia de tiempo de ejecución porque esta sobrecarga no está permitida.
Gábor Angyal

Ahh - buen punto! ¿Alguien puede desmontar el código anterior y demostrar que estoy en lo correcto o incorrecto? Soy terrible leyendo la asamblea, pero tengo curiosidad por saberlo.
Shmoken

Bien, desarme el código usando "out" y "ref" y no puedo ver ninguna diferencia para mi método DoSomething:
Shmoken
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.