stored - ref parameter c#
¿Ejemplos del mundo real donde los parámetros de "salida" de C#son útiles? (9)
Claro, eche un vistazo a cualquiera de los métodos TryParse
, como int.TryParse
:
La idea es que realmente desea dos piezas de información: si una operación de análisis tuvo éxito (el valor de retorno), y , de ser así, cuál fue el resultado real (el parámetro de out
).
Uso:
string input = Console.ReadLine();
int value;
// First we check the return value, which is a bool
// indicating success or failure.
if (int.TryParse(input, out value))
{
// On success, we also use the value that was parsed.
Console.WriteLine(
"You entered the number {0}, which is {1}.",
value,
value % 2 == 0 ? "even" : "odd"
);
}
else
{
// Generally, on failure, the value of an out parameter
// will simply be the default value for the parameter''s
// type (e.g., default(int) == 0). In this scenario you
// aren''t expected to use it.
Console.WriteLine(
"You entered ''{0}'', which is not a valid integer.",
input
);
}
Muchos desarrolladores se quejan de out
parámetros como un "olor de código"; pero pueden ser, con mucho, la opción más adecuada en muchos escenarios. Un ejemplo moderno muy importante sería el código multiproceso; a menudo es necesario un parámetro de out
para permitir operaciones "atómicas" donde un valor de retorno no sería suficiente.
Considere, por ejemplo, Monitor.TryEnter(object, ref bool)
, que adquiere un bloqueo y establece un bool
atómica, algo que no sería posible solo a través de un valor de retorno, ya que la adquisición del bloqueo necesariamente ocurriría antes de que el valor de retorno se asignara a un Variable bool
. (Sí, técnicamente ref
y out
no son lo mismo, pero están muy cerca).
Otro buen ejemplo sería algunos de los métodos disponibles para las clases de colección en el System.Collections.Concurrent
namespace new to .NET 4.0; estos proporcionan operaciones similares a la seguridad de subprocesos como ConcurrentQueue<T>.TryDequeue(out T)
y ConcurrentDictionary<TKey, TValue>.TryRemove(TKey, out TValue)
.
Estoy leyendo sobre las construcciones de programación del núcleo C # y me out
difícil envolver mi cabeza alrededor del modificador de parámetro de out
. Sé lo que hace leyendo, pero estoy tratando de pensar en un escenario cuando lo usaría.
¿Puede alguien darme un ejemplo del mundo real? Gracias.
Como han dicho otros, los parámetros de salida nos permiten devolver más de un valor de una llamada de método sin tener que ajustar los resultados en estructura / clase.
La adición de los métodos xxx.TryParse simplificó en gran medida la codificación necesaria para convertir entre un valor de cadena (con frecuencia desde la interfaz de usuario) y un tipo primitivo.
Un ejemplo de lo que podría haber tenido que escribir para lograr la misma funcionalidad está aquí:
/// <summary>
/// Example code for how <see cref="int.TryParse(string,out int)"/> might be implemented.
/// </summary>
/// <param name="integerString">A string to convert to an integer.</param>
/// <param name="result">The result of the parse if the operation was successful.</param>
/// <returns>true if the <paramref name="integerString"/> parameter was successfully
/// parsed into the <paramref name="result"/> integer; false otherwise.</returns>
public bool TryParse(string integerString, out int result)
{
try
{
result = int.Parse(integerString);
return true;
}
catch (OverflowException)
{
// Handle a number that was correctly formatted but
// too large to fit into an Int32.
}
catch (FormatException)
{
// Handle a number that was incorrectly formatted
// and so could not be converted to an Int32.
}
result = 0; // Default.
return false;
}
Las dos comprobaciones de excepción que se evitan aquí hacen que el código de llamada sea mucho más legible. Creo que las implementaciones reales de .NET evitan las excepciones por completo, por lo que también funcionan mejor. De manera similar, este ejemplo muestra cómo IDictionary.TryGetValue (...) hace que el código sea más simple y más eficiente:
private readonly IDictionary<string,int> mDictionary = new Dictionary<string, int>();
public void IncrementCounter(string counterKey)
{
if(mDictionary.ContainsKey(counterKey))
{
int existingCount = mDictionary[counterKey];
mDictionary[counterKey] = existingCount + 1;
}
else
{
mDictionary.Add(counterKey, 1);
}
}
public void TryIncrementCounter(string counterKey)
{
int existingCount;
if (mDictionary.TryGetValue(counterKey, out existingCount))
{
mDictionary[counterKey] = existingCount + 1;
}
else
{
mDictionary.Add(counterKey, 1);
}
}
Y todo gracias al parámetro out.
Hay muchos escenarios en los que lo usaría, pero el principal sería donde su método necesita devolver más de un parámetro. Tomemos, por ejemplo, los métodos TryParse
en el tipo int
. En este caso, en lugar de lanzar una excepción, se devuelve un bool como indicador de éxito / fallo y el int analizado se devuelve como parámetro de salida. si tuviera que llamar a int.Parse(...)
podría lanzar una excepción.
string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}
La principal motivación para usar un parámetro de out
es permitir que una función devuelva múltiples valores a la persona que llama y todos los demás proporcionaron ejemplos en el marco. Tomaré un enfoque diferente para responder a su pregunta explorando el razonamiento detrás de tener parámetros en primer lugar. No escribiré ejemplos reales sino los describiré.
Normalmente, solo tiene un mecanismo para devolver valores, el valor de retorno de la función. Claro que también puedes usar variables globales (estáticas) o de instancia, pero eso no es muy práctico ni seguro de hacer en general (por razones que no explicaré aquí). Antes de .NET 3.5 , no había una forma realmente práctica de devolver múltiples valores desde una función. Si out
modificadores out
o ref
no estuvieran disponibles, tendría algunas opciones:
Si todos sus valores tuvieran el mismo tipo, podría devolver alguna recopilación de los valores. Esto está perfectamente bien en la mayoría de los casos, podría devolver una serie de números, listas de cadenas, lo que sea. Esto es perfecto si todos los valores estaban relacionados exactamente de la misma manera. es decir, Todos los números eran el número de elementos en un contenedor, o la lista era de nombres de invitados en una fiesta. Pero, ¿y si los valores que devolviste representaban cantidades diferentes? ¿Y si tuvieran diferentes tipos? Una lista de objetos podría contenerlos a todos, pero no es una forma muy intuitiva de manipular ese tipo de datos.
En el caso de que necesite devolver varios valores de diferentes tipos, la única opción práctica que tuvo fue crear un nuevo tipo de clase / estructura para encapsular todos estos valores y devolver una instancia de ese tipo. Al hacerlo, podría devolver valores fuertemente tipados con nombres intuitivos y podría devolver múltiples valores de esta manera. El problema es que para obtener eso, tenía que definir el tipo con un nombre específico y todo para poder devolver múltiples valores. ¿Qué sucedería si quisiera devolver solo dos valores que fueran lo suficientemente simples para que no sea práctico crear un tipo para ellos? Tienes un par de opciones más de nuevo:
Puede crear un conjunto de tipos genéricos para contener una cantidad fija de valores de diferentes tipos (como una tupla en lenguajes funcionales). Pero no es tan atractivo hacerlo de una manera reutilizable ya que no era parte del marco en ese momento. Podría colocarse en una biblioteca, pero ahora agrega una dependencia de esa biblioteca solo por el simple hecho de estos tipos simples. (solo agradezca que .NET 4.0 ahora incluya el tipo
Tuple
) Pero esto todavía no resuelve el hecho de que estos son valores simples, lo que significa una mayor complejidad para una tarea simple.La opción que se usó fue incluir un modificador de
out
que permite a la persona que llama pasar una "referencia" a una variable para que la función pueda establecer la variable referenciada como otra forma de devolver un valor. Esta forma de devolver valores también estaba disponible en C y C ++ de muchas maneras por las mismas razones y jugó un papel en influir en esta decisión. Sin embargo, la diferencia en C # es que para un parámetro deout
, la función debe establecer el valor en algo. Si no lo hace, se produce un error de compilación. Esto hace que este error sea menos propenso, ya que al tener un parámetro deout
, le está prometiendo a la persona que llama que establecerá el valor en algo y que puede usarlo, el compilador se asegura de que cumpla con esa promesa.
Una nota sobre el uso típico del modificador de out
(o ref
), será raro ver más de uno o dos parámetros de out
. En esos casos, casi siempre será una idea mucho mejor crear el tipo de encapsulación. Normalmente lo utilizarías si necesitaras un valor más para devolver.
Sin embargo, desde C # -3.0 / .NET-3.5 con la introducción de tipos anónimos y tuplas introducidas en .NET 4.0, estas opciones proporcionaron métodos alternativos para devolver múltiples valores de diferentes tipos de manera más fácil (y más intuitiva).
Las otras respuestas muestran cómo out
parámetros le permiten devolver más de un valor de un método.
Me gustaría describir otra ventaja distintiva de out
parámetros: mejorar la interfaz pública de una clase haciendo que los errores de uso sean menos probables .
Primer intento (defectuoso)
Consideremos la siguiente interfaz:
interface IUndoableCommand
{
void Execute();
IUndoableCommand GetUndoCommand();
}
Este es el patrón de comando para los comandos que se pueden deshacer. Sin embargo, algo está mal con este diseño de interfaz: le permite escribir código que deshace un comando antes de que se haya ejecutado:
someCommand.GetUndoCommand().Execute();
someCommand.Execute();
// ^ this is obviously wrong, but will compile.
Segundo intento (todavía defectuoso)
Así que intentemos evitar este error volviendo a diseñar la interfaz:
interface IUndoableCommand
{
IUndoableCommand Execute();
}
Ahora solo tiene acceso al comando "deshacer" una vez que se haya ejecutado el comando que no se puede deshacer.
var undoCommand = someCommand.Execute();
undoCommand.Execute();
// ^ this is better, but has other problems...
Si bien este diseño no es explícitamente malo, tiene dos fallas:
No es muy lógico que un método
Execute
devuelva algo. Si devuelve algo, es probable que piense que es algún tipo de indicador de éxito ("result
"), pero no otro comando "deshacer".Le permite "encadenar" llamadas a
Execute
en la forma de:someCommand.Execute().Execute(); // ^^^^^^^^^^^^^^^^^^^^ // guess what this does!?
Al contrario de lo que parece, algunos
someCommand
no se ejecutarán dos veces; será efectivamente cancelado.
Intento final out
parámetro (exitoso)
Así que, una vez más, intentemos mejorar la interfaz reemplazando el valor de retorno con un parámetro de out
:
interface IUndoableCommand
{
void Execute(out IUndoableCommand undoCommand);
}
Este diseño no tiene ninguna de las desventajas que se muestran arriba:
- No puede llamar al comando "deshacer" antes del comando real.
-
Execute
no tiene valor de retorno (o podría tener un valor de retorno de indicador de éxito), como debería ser. No puede encadenar el método
Execute
del comando "deshacer" al métodoExecute
del comando real.IUndoableCommand undoCommand; someCommand.Execute(out undoCommand); undoCommand.Execute(… /* out someCommand */);
(Por supuesto, una vez que tenga referencias a ambos comandos, aún podría hacer travesuras, como llamar dos veces al undoCommand
, pero probablemente no haya forma de detener eso en tiempo de compilación).
Los parámetros de salida se encuentran en todo el marco .NET. Algunos de los usos que veo con más frecuencia son los métodos int.TryParse , que devuelven un valor booleano (que indica si el análisis fue válido) y el resultado real se devuelve a través del parámetro de salida. Si bien también es un lugar muy común para usar una clase cuando necesita devolver varios valores, en un ejemplo como este es un poco torpe. Para obtener más información sobre los parámetros de salida, consulte el artículo de Jon Skeet sobre el paso de parámetros en C # .
Simple, cuando tienes un método que devuelve más de un valor. Uno de los casos más "famosos" es Dictionary.TryGetValue :
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("For key = /"tif/", value = {0}.", value);
}
else
{
Console.WriteLine("Key = /"tif/" is not found.");
}
//out key word is used in function instead of return. we can use multiple parameters by using out key word
public void outKeyword(out string Firstname, out string SecondName)
{
Firstname = "Muhammad";
SecondName = "Ismail";
}
//on button click Event
protected void btnOutKeyword_Click(object sender, EventArgs e)
{
string first, second;
outKeyword(out first, out second);
lblOutKeyword.Text = first + " " + second;
}
bool Int32.TryParse(String, out Int);
o algo similar como Dictionary.TryGetValue.
Pero consideraría que esta no es una práctica demasiado buena como para emplearla, por supuesto, usar las que proporciona la API como la Int32 para evitar Try-Catch es una excepción.