2009-07-23 23 views
43

No entiendo cuándo se debe usar un parámetro de salida, personalmente envuelvo el resultado en un nuevo tipo si necesito devolver más de un tipo, me resulta mucho más fácil trabajar que hacerlo.¿Cuándo debería usar los parámetros?

he visto método como este,

public void Do(int arg1, int arg2, out int result) 

¿existen casos en los que realmente tiene sentido?

¿qué tal TryParse, por qué no devolver un tipo ParseResult? o en el marco más nuevo, ¿devuelve un tipo anulable?

+1

Puede escribir un contenedor alrededor de TryParse si no le gusta. – tuinstoel

+0

BTW, también hay un método Parse, que no tiene ningún parámetro out y simplemente devuelve un valor. Si la cadena no se puede convertir al tipo, se lanza una excepción. – devuxer

+3

Tener un tipo de retorno de vacío mientras usa un parámetro de salida único no tiene sentido, no. Sin embargo, ve algunas de las respuestas para buenos usos. – Thorarin

Respuesta

24

La salida es buena cuando tiene una función TryNNN y está claro que el parámetro de salida siempre se establecerá aunque la función no funcione. Esto le permite confiar en el hecho de que la variable local que declare se establecerá en lugar de tener que colocar cheques más adelante en su código contra nulo. (Un comentario a continuación indica que el parámetro podría establecerse en null, por lo que es posible que desee verificar la documentación de la función que está llamando para asegurarse de que este sea el caso o no). Hace que el código sea un poco más claro y fácil leer. Otro caso es cuando se necesita para devolver algunos datos y un estado de la condición del método como:

public bool DoSomething(int arg1, out string result); 

En este caso, el retorno puede indicar si la función tuvo éxito y el resultado se almacena en el parámetro de salida. Es cierto que este ejemplo se concibe porque puede diseñar una forma en la que la función simplemente devuelva string, pero se entiende la idea.

Una desventaja es que usted tiene que declarar una variable local para usarlos:

string result; 
if (DoSomething(5, out result)) 
    UpdateWithResult(result); 

En lugar de:

UpdateWithResult(DoSomething(5)); 

Sin embargo, que ni siquiera puede ser una desventaja, que depende de la diseño que estás buscando. En el caso de DateTime, se proporcionan ambos medios (Parse y TryParse).

+0

En realidad, ocurre todo lo contrario con respecto a esperar que el parámetro de salida permanezca sin cambios después de que la llamada "falla". En la mayoría de los casos, el parámetro de salida no debe inicializarse antes de pasar a la función. – Samuel

+3

"el parámetro de salida no cambiará si la función no funciona". - El parámetro out wil * always * se cambiará a menos que la función arroje una excepción. Un método con un parámetro out no se compilará si tiene una ruta de ejecución que no asigna un valor al parámetro out. Por lo tanto, la inicialización 'string result = "";' en tu segundo ejemplo es innecesario. – Joe

+1

Lo que dijo Samuel. Establecer un parámetro de salida antes de salir de un método es * obligatorio *. Si desea que un valor no se modifique en algunos casos, use ref en lugar de out. – Thorarin

0

Crear un tipo solo para devolver valores me suena un poco doloroso :-) Primero tendré que crear un tipo para devolver el valor y luego en el método de llamada. Asigné el valor del tipo devuelto a la variable real eso lo necesita

Los parámetros de salida son simuladores de usar.

0

Sí, tiene sentido. Toma esto por ejemplo.

String strNum = "-1"; 
Int32 outNum; 

if (Int32.TryParse(strNum, out outNum)) { 
    // success 
} 
else { 
    // fail 
} 

¿Qué podría devolver si la operación fallara en una función normal con un valor de retorno? Definitivamente no podría devolver -1 para representar un error, porque entonces no habría diferencia entre el valor de falla de retorno y el valor real que se estaba analizando para comenzar. Es por eso que devolvemos un valor booleano para ver si tuvo éxito, y si lo hizo, entonces ya tenemos nuestro valor de "retorno" asignado de manera segura.

+0

puede devolver el tipo no existente ParsedResult : {bool Succes {get;}, T Result {get;} –

+1

Es cierto, pero esto requiere una sobrecarga adicional al crear un tipo de valor devuelto, cuando no es necesario. TryParse ya tiene toda la funcionalidad que podría necesitar para este escenario. –

0

Me molesta que no pueda pasar nulo al parámetro out para las funciones TryParse.

Aún así, en algunos casos prefiero devolver un nuevo tipo con dos datos. Especialmente cuando no están relacionados en su mayor parte o una pieza solo es necesaria para una sola operación un momento después. Cuando necesito guardar el valor resultante de una función TryParse, realmente me gusta tener un parámetro out en lugar de una clase aleatoria ResultAndValue con la que tengo que lidiar.

5

Bueno, como con la mayoría de las cosas depende. Veamos las opciones

  • que podría devolver lo que desea que sea el valor de retorno de la función
  • si usted quiere devolver múltiples valores o la función ya tiene un valor de retorno, se puede utilizar fuera params o crear un nuevo tipo de material compuesto que expone todos estos valores como propiedades

en el caso de TryParse, utilizando un parámetro a cabo es eficiente - usted no tiene que crear un nuevo tipo que sería 16B de la cabeza (en 32b máquinas) o incurrir en el costo de perforación de tenerlos recogidos basura después de la llamada. Por ejemplo, se podría llamar a TryParse desde dentro de un bucle, de modo que aquí se usa la regla de params.
Para funciones que no se llamarían dentro de un bucle (es decir, el rendimiento no es una preocupación importante), devolver un solo objeto compuesto podría ser 'más limpio' (subjetivo para el espectador). Ahora con tipos anónimos y tipado dinámico, puede ser aún más fácil.

Nota:

  1. out params tiene algunas reglas que deben seguirse es decir, el compilador se asegurará de que la función hace inicializar el valor antes de que salga. Por lo tanto, TryParse tiene que establecer el parámetro param a cierto valor incluso si la operación de análisis falló
  2. El patrón TryXXX es un buen ejemplo de cuándo usar params: Int32.TryParse se introdujo porque las personas se quejaron del golpe de captura de excepciones para saber si el análisis falló También lo más probable que haría en caso de que el análisis tuviera éxito, es obtener el valor analizado. Usar un parámetro param significa que no tiene que hacer otra llamada a Parse
+0

No creo que haya una forma fácil de devolver el tipo anónimo, ¿verdad? – chikak

+0

Pre C# 4.0, el problema está en consumir estos valores de retorno de tipo anon. El método podría devolver un objeto anónimo como un 'objeto', pero el llamador no puede hacer un returnValue.FirstEmbeddedValue aunque exista en el objeto. Se necesita una resolución de método dinámico para que esto funcione ... – Gishu

+0

@Gishu: ¿es un tipo compuesto lo mismo que una clase? – Radmation

3

Creo que es útil para situaciones donde tiene que devolver tanto un valor lógico y un valor, como TryParse, pero sería bueno si el compilador permitiría que algo como esto:

bool isValid = int.TryParse("100", out int result = 0); 
+0

Me gusta la declaración en línea, nunca pensé en hacerlo de esa manera –

+1

¡Ja! Si solo :-) – ScottWelker

+3

Esto ahora está disponible en C# 7 – AnonUser

0

utilizo cabo parámetros veces para facilitar la lectura, al leer el método el nombre es más importante que cualquiera que sea el resultado del método, especialmente para los métodos que ejecutan comandos además de devolver resu lts.

StatusInfo a, b, c; 

Initialize(out a); 
Validate(a, out b); 
Process(b, out c); 

vs

StatusInfo a = Initialize(); 
StatusInfo b = Validate(a); 
StatusInfo c = Process(b); 

Al menos para mí, me puso mucho énfasis en los primeros caracteres de cada línea cuando estoy escaneando. Puedo ver fácilmente qué está sucediendo en el primer ejemplo después de reconocer que algunas variables de "StatusInfo" están declaradas. En el segundo ejemplo, lo primero que veo es que se recupera un montón de StatusInfo.Tengo que escanear por segunda vez para ver qué tipo de efectos pueden tener los métodos.

+4

en realidad, ¡creo que el primero parece mucho peor! –

+0

Cambié los nombres a algo que se asemeja más al código del mundo real. Puedo desenterrar un ejemplo real si todavía se ve mal. –

+2

Acepto que el uso de parámetros 'out' puede mejorar la legibilidad. – JTech

0

Si siempre crea un tipo, entonces puede terminar con un montón de desorden en su aplicación.

Como dije aquí, un caso de uso típico es un TrySomething Método en el que desea devolver un bool como indicador de éxito y luego el valor real. También me parece un poco más limpio en una declaración if: las tres opciones tienen aproximadamente la misma LOC de todos modos.

int myoutvalue; 
if(int.TryParse("213",out myoutvalue){ 
    DoSomethingWith(myoutvalue); 
} 

vs. 

ParseResult<int> myoutvalue = int.TryParse("213"); 
if (myoutvalue.Success) { 
    DoSomethingWith(myoutvalue.Value); 
} 

vs. 

int? myoutvalue = int.TryParse("213"); 
if(myoutvalue.HasValue){ 
    DoSomethingWith(myoutvalue.Value); 
} 

En cuanto a la "¿Por qué no devolver un tipo anulable": existe TryParse desde 1.x Marco, mientras que los tipos anulables vinieron con 2,0 (Dado que requieren los genéricos). Entonces, ¿por qué romper innecesariamente la compatibilidad o comenzar a introducir inconsistencias entre TryParse en algunos tipos? Siempre puede escribir su propio Método de extensión para duplicar la funcionalidad ya existente (Consulte Eric Lipperts Post en un tema no relacionado que incluye algún razonamiento detrás de hacer/no hacer cosas)

Otro caso de uso es si debe devolver múltiples valores no relacionados, aunque si haces eso, esto debería disparar una alarma de que tu método probablemente esté haciendo demasiado. Por otro lado, si su Método es algo así como una costosa base de datos o llamada de servicio web y desea almacenar en caché el resultado, puede tener sentido hacerlo. Claro, podría crear un tipo, pero nuevamente, eso significa un tipo más en su aplicación.

2

Definitivamente, salir parámetros están destinados a ser utilizados cuando se tiene un método que tiene que devolver más de un valor, en el ejemplo informados:

public void Do(int arg1, int arg2, out int result) 

No tiene mucho sentido usar una fuera de parámetros, ya que sólo está devolviendo un valor, y que el método podría utilizarse mejor si se quita el parámetro de salida y poner un valor int retorno:

public int Do(int arg1, int arg2) 

Hay algunas cosas buenas de fuera parámetros:

  1. Los parámetros de salida se consideran inicialmente no asignados.
    • Cada parámetro out debe ser definitivamente asignado antes se devuelve el método, el código se compila si se olvida de una asignación.

En conclusión, Básicamente intento utilizar a cabo params en mi API privada para evitar la creación de tipos separados para envolver múltiples valores de retorno, y en mi API pública, sólo los uso en los métodos que coincidan con el patrón TryParse.

1

Llegó tarde con una respuesta, lo sé. out (y ref también) también es muy útil si no desea que su método haga una instancia de un nuevo objeto para devolver. Esto es muy relevante en los sistemas de alto rendimiento en los que desea lograr un rendimiento de microsegundo inferior para su método. instanciar es relativamente costoso visto desde una perspectiva de acceso a la memoria.

Cuestiones relacionadas