type parameter c# ref

parameter - ref out c#



En C#, ¿dónde usas "ref" delante de un parámetro? (10)

¿Qué pasa si desea devolver varios objetos , que por alguna razón desconocida no están unidos en un solo objeto?

void GetXYZ( ref object x, ref object y, ref object z);

EDITAR: divo sugirió que usar parámetros OUT sería más apropiado para esto. Tengo que admitir que tiene un punto. Dejaré esta respuesta aquí para, para el registro, esta es una solución inadecuada. OUT triunfa sobre el REF en este caso.

Ya hay una serie de preguntas sobre la definición de los parámetros "ref" y "out" pero parecen un mal diseño. ¿Hay casos en los que crees que ref es la solución correcta?

Parece que siempre se podría hacer algo más limpio. ¿Puede alguien darme un ejemplo de dónde esta sería la "mejor" solución para un problema?


Bueno, ref se usa generalmente para casos especializados, pero no lo llamaría redundante o una característica heredada de C #. Lo verás (y out ) usado mucho en XNA, por ejemplo. En XNA, Matrix es una struct y bastante masiva (creo que 64 bytes) y generalmente es mejor si se pasa a las funciones usando ref para evitar copiar 64 bytes, pero solo 4 u 8. ¿Una función especializada en C #? Ciertamente. ¿De no usar mucho más o indicativo de mal diseño? No estoy de acuerdo


Creo que los mejores usos son aquellos que usualmente ves; debe tener tanto un valor como un "indicador de éxito" que no sea una excepción de una función.


Hay un caso claro cuando debe usar la palabra clave ''ref''. Si el objeto se define pero no se crea fuera del alcance del método que pretende llamar Y se supone que el método al que desea llamar hace lo ''new'' para crearlo, debe usar ''ref'' . por ejemplo, {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} {object a; Funct(a);} {Funct(object o) {o = new object; o.name = "dummy";} NO hará nada con el object ''a'' ni se quejará al respecto ni en compilación ni en tiempo de ejecución. Simplemente no hará nada. {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} {object a; Funct(ref a);} {Funct(object ref o) {o = new object(); o.name = "dummy";} resultará en que ''a'' sea ​​un nuevo objeto con el nombre de "dummy ". Pero si el ''new'' ya estaba hecho, entonces no se necesita ref (pero funciona si se suministra). {object a = new object(); Funct(a);} {Funct(object o) {o.name = "dummy";}


La razón obvia para usar la palabra clave "ref" es cuando quiere pasar una variable por referencia. Por ejemplo, pasar un tipo de valor como System.Int32 a un método y modificar su valor real. Un uso más específico podría ser cuando desea intercambiar dos variables.

public void Swap(ref int a, ref int b) { ... }

La razón principal para usar la palabra clave "out" es devolver varios valores de un método. Personalmente prefiero envolver los valores en una estructura o clase especializada, ya que usar el parámetro out produce un código bastante feo. Los parámetros pasados ​​con "out" - es como "ref" - pasados ​​por referencia.

public void DoMagic(out int a, out int b, out int c, out int d) { ... }


P / Invocar es el único lugar donde realmente puedo pensar en un lugar donde debes usar ref o fuera. En otros casos, pueden ser convenientes, pero como usted dijo, generalmente hay otra forma más limpia.


Un área es el uso de pequeñas funciones de utilidad, como:

void Swap<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; }

No veo ninguna alternativa ''más limpia'' aquí. Por supuesto, esto no es exactamente el nivel de arquitectura.


Un patrón de diseño donde la ref es útil es un visitante bidireccional.

Supongamos que tiene una clase de Storage que se puede utilizar para cargar o guardar valores de varios tipos primitivos. Se encuentra en modo de Load o modo de Save . Tiene un grupo de métodos sobrecargados llamados Transfer , y aquí hay un ejemplo para tratar con los valores int .

public void Transfer(ref int value) { if (Loading) value = ReadInt(); else WriteInt(value); }

Habría métodos similares para otros tipos primitivos: bool , string , etc.

Luego, en una clase que necesita ser "transferible", escribirías un método como este:

public void TransferViaStorage(Storage s) { s.Transfer(ref _firstName); s.Transfer(ref _lastName); s.Transfer(ref _salary); }

Este mismo método puede cargar los campos desde el Storage o guardarlos en el Storage , según el modo en que se encuentre el objeto de Storage .

En realidad, solo está enumerando todos los campos que deben transferirse, por lo que se acerca mucho a la programación declarativa en lugar de a la imperativa. Esto significa que no necesita escribir dos funciones (una para leer, otra para escribir) y dado que el diseño que uso aquí depende de la orden, es muy útil saber con seguridad que los campos siempre se leerán / Escrito en idéntico orden.

El punto general es que cuando un parámetro está marcado como ref , no sabe si el método lo va a leer o escribir en él, y esto le permite diseñar clases de visitantes que funcionan en una de dos direcciones, que se pretende que sean se llama de forma simétrica (es decir, con el método visitado que no necesita saber en qué modo de dirección está operando la clase visitante).

Comparación: Atributos + Reflexión

¿Por qué hacer esto en lugar de atribuir los campos y usar la reflexión para implementar automáticamente el equivalente de TransferViaStorage ? Porque a veces la reflexión es lo suficientemente lenta como para ser un cuello de botella (pero siempre un perfil para estar seguro de esto; casi nunca es cierto, y los atributos están mucho más cerca del ideal de la programación declarativa).


El uso real para esto es cuando creas una estructura. Las estructuras en C # son tipos de valor y, por lo tanto, siempre se copian completamente cuando se pasan por valor. Si necesita pasarlo por referencia, por ejemplo, por razones de rendimiento o porque la función necesita realizar cambios en la variable, debe utilizar la palabra clave ref.

Podría ver si alguien tiene una estructura con 100 valores (obviamente ya hay un problema), es probable que desee pasarla por referencia para evitar que se copien 100 valores. Eso y devolver esa estructura grande y escribir sobre el valor anterior probablemente tendrá problemas de rendimiento.


En mi opinión, ref compensó en gran medida la dificultad de declarar nuevos tipos de utilidad y la dificultad de "agregar información" a la información existente, que son cosas que C # ha dado grandes pasos para abordar desde su origen a través de LINQ, genéricos y tipos anónimos. .

Así que no, creo que ya no hay muchos casos de uso claros. Creo que es en gran parte una reliquia de cómo se diseñó originalmente el lenguaje.

Creo que aún tiene sentido (como se mencionó anteriormente) en el caso de que necesite devolver algún tipo de código de error de una función, así como un valor de retorno, pero nada más (por lo que un tipo más grande no está realmente justificado). ) Si estuviera haciendo esto por todas partes en un proyecto, probablemente definiría algún tipo de envoltorio genérico para algo-más-error-código, pero en cualquier instancia dada, la ref y la out están bien.