C # 4.0 argumentos opcionales de salida / referencia

212

¿C # 4.0 permite opcionales outo refargumentos?

Joe Daley
fuente
2
Weell, C ++ efectivamente los tiene para parámetros "fuera": puede tener un argumento de dirección inicializado como nulo y es bastante común escribir código de biblioteca que solo llenará dicha estructura de retorno si el puntero no es nulo. Ese es un idioma que vuelve a usar nulo para "argumentos opcionales" en las API de C.
Andy Dent
53
@ Ed y todos: ¿por qué esto no tiene sentido? Si una función "devuelve" un valor a través de "out", no quiero verme obligado a aceptarlo. Ahora sé que, por razones técnicas, el compilador todavía tiene que pasar algo, pero no hay razón por la que no pueda simplemente crear un local ficticio para mí a mis espaldas.
Roman Starkov
55
Tal vez no tiene sentido desde el punto de vista de cómo se implementan las cosas o qué es realmente un parámetro opcional. Pero como dijo romkyns, sería realmente bueno tener "argumentos de salida opcionales": analícelo en inglés en lugar de CLR y se vuelve razonable y deseable en la OMI.
Adam Tolley
99
C # no, pero VB.NET sí.
Jason
55
Esto ha sido golpeado hasta la muerte, sin embargo, no puedo evitar mencionar mi apoyo a los argumentos opcionales. Me he acostumbrado bastante a los argumentos opcionales por referencia estableciendo un nullvalor predeterminado ( vengo de PHP ) y probando para nullproceder a completar el argumento ( para aquellos familiares, piensenpreg_match() ) De todos modos, aunque entiendo desde un punto técnico esto puede ser actualmente imposible, y que PHP y C # son bastante incomparables, todavía sería una herramienta " agradable " tener disponible.
Dan Lugg

Respuestas:

93

Como ya se mencionó, esto simplemente no está permitido y creo que tiene mucho sentido. Sin embargo, para agregar más detalles, aquí hay una cita de la Especificación C # 4.0 , sección 21.1:

Los parámetros formales de constructores, métodos, indexadores y tipos de delegados pueden declararse opcionales:

parámetro fijo:
    atributos opt parámetro-modificador opt tipo identificador argumento -predeterminado opt
argumento-predeterminado:
    = expresión

  • Un parámetro fijo con un argumento predeterminado es un parámetro opcional , mientras que un parámetro fijo sin un argumento predeterminado es un parámetro obligatorio .
  • Un parámetro requerido no puede aparecer después de un parámetro opcional en una lista de parámetros formales .
  • Un parámetro refo outno puede tener un argumento predeterminado .
Tomás Petricek
fuente
Alternativamente, puede crear una sobrecarga con un parámetro de ref / out. Sí, tendrá dos definiciones de función, pero logrará lo que busca
Chad,
201

No.

Una solución alternativa es sobrecargar con otro método que no tiene parámetros out / ref, y que simplemente llama a su método actual.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

Actualización: si tiene C # 7.0 , puede simplificar:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Gracias @Oskar / @Reiner por señalar esto).

Dunc
fuente
66
¿Alguna idea para una solución más elegante que declarar temp / dummy?
Louis Rhys
20
¿Qué no es elegante sobre eso? Se ve perfectamente decente para mí.
Neutrino
27
Quizás decente, pero elegante definitivamente está en otra liga. El hecho de que no haya una solución mejor no significa que esto sea lo último en decretos.
o0 '.
77
Aguanta @ o0 '. En C # 7.0 usted será capaz de hacer esto: return SomeMethod(out string temp). Ver más aquí: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar
77
En C # 7.0 puede usar una variable temporal de solo escritura como:return SomeMethod(out _);
Reiner
64

No, pero otra gran alternativa es hacer que el método use una clase de plantilla genérica para parámetros opcionales de la siguiente manera:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Entonces puede usarlo de la siguiente manera:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}
Robin R
fuente
19
Es una solución muy razonable, pero una cosa a tener en cuenta es que el compilador no impondrá el requisito de que se asigne el parámetro out antes de salir del método.
Ken Smith
2
Me gusta, pero si no desea crear una nueva clase, puede simularla pasando una sola matriz de elementos.
zumalifeguard
30

En realidad, hay una manera de hacer esto que C # permite. Esto vuelve a C ++, y más bien viola la agradable estructura orientada a objetos de C #.

¡USE ESTE MÉTODO CON PRECAUCIÓN!

Esta es la forma en que declara y escribe su función con un parámetro opcional:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Luego llame a la función de esta manera:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

Para que esto se compile, deberá habilitar el código inseguro en las opciones del proyecto. Esta es una solución realmente hacky que generalmente no debería usarse, pero si por alguna decisión extraña, arcana, misteriosa e inspirada en la administración, REALMENTE necesita un parámetro de salida opcional en C #, entonces esto le permitirá hacer exactamente eso.

wizard07KSU
fuente
6

ICYMI: incluidas en las nuevas funciones para C # 7.0 enumeradas aquí , "descartes" ahora está permitido como parámetros externos en forma de _, para permitirle ignorar los parámetros que no le interesan:

p.GetCoordinates(out var x, out _); // I only care about x

PD: si también estás confundido con la parte "out var x", lee también la nueva función sobre "Out Variables" en el enlace.

jbdeguzman
fuente
¿es _ o * "p. GetCoordinates (out int x, out *); // Solo me importa x"
Onur Topal
Parece que estaba buscando una versión anterior del documento.
Onur Topal el
2

No, pero puede usar un delegado (por ejemplo Action) como alternativa.

Inspirado en parte por la respuesta de Robin R al enfrentar una situación en la que pensé que quería un parámetro de salida opcional, en su lugar utilicé un Actiondelegado. He tomado prestado su código de ejemplo para modificarlo para usarlo Action<int>a fin de mostrar las diferencias y similitudes

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

Esto tiene la ventaja de que la variable opcional aparece en la fuente como un int normal (el compilador la envuelve en una clase de cierre, en lugar de que la envuelvamos explícitamente en una clase definida por el usuario).

La variable necesita una inicialización explícita porque el compilador no puede suponer que Actionse invocará antes de que finalice la llamada a la función.

No es adecuado para todos los casos de uso, pero funcionó bien para mi caso de uso real (una función que proporciona datos para una prueba unitaria, y donde una nueva prueba unitaria necesitaba acceso a algún estado interno no presente en el valor de retorno).

Steve
fuente
0

Use un método sobrecargado sin el parámetro out para llamar al que tiene el parámetro out para C # 6.0 y versiones inferiores. No estoy seguro de por qué un C # 7.0 para .NET Core es incluso la respuesta correcta para este hilo cuando se le preguntó específicamente si C # 4.0 puede tener un parámetro de salida opcional. ¡La respuesta es no!

Mr_CSharp
fuente
-2

¿Qué tal así?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

Aún debe pasar un valor al parámetro desde C #, pero es un parámetro de referencia opcional.

Aaron L.
fuente
8
"Aún debe pasar un valor al parámetro desde C #" ... Esto hace que no sea opcional.
Arturo Torres Sánchez
C # ignora la [Optional]anotación. Esto no ayuda
ToolmakerSteve
-4
void foo(ref int? n)
{
    return null;
}
usuario6469927
fuente
1
Agregue alguna explicación en su código para que todos entiendan el concepto fácilmente
techspider
1
Aunque este código puede responder la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo responde la pregunta mejoraría significativamente su valor a largo plazo. Por favor, editar su respuesta a añadir un poco de explicación.
Toby Speight
2
Esto dará como resultado un error de sintaxis ya que el método tiene un tipo de retorno nulo. Además, no responde a la pregunta.
Nathan Montez el