¿Cómo puede usar parámetros opcionales en C #?

493

Nota: Esta pregunta se hizo en un momento en que C # aún no admitía parámetros opcionales (es decir, antes de C # 4).

Estamos creando una API web que se genera mediante programación a partir de una clase C #. La clase tiene un método GetFooBar(int a, int b)y la API tiene un método que GetFooBartoma parámetros de consulta como &a=foo &b=bar.

Las clases deben admitir parámetros opcionales, que no se admiten en C #, el lenguaje. ¿Cuál es el mejor enfoque?

Kalid
fuente
77
O espere a que se lance la unidad C # 4.0. Los parámetros opcionales son compatibles.
Micah

Respuestas:

1055

Sorprendido, nadie mencionó los parámetros opcionales de C # 4.0 que funcionan así:

public void SomeMethod(int a, int b = 0)
{
   //some code
}

Editar: Sé que en el momento en que se hizo la pregunta, C # 4.0 no existía. Pero esta pregunta todavía ocupa el puesto número 1 en Google por "argumentos opcionales C #", así que pensé: esta respuesta vale la pena estar aquí. Lo siento.

Alex
fuente
58
En el momento en que se hizo la pregunta, C # 4.0 no existía.
Forrest Marvez
9191
Sí, pasé por alto esto, lo siento. Pero esta cuestión todavía ocupa el lugar # 1 en Google de "C # argumentos opcionales" de todos modos - esto vale la pena estar aquí :) respuesta
Alex
45
Bien por usted para proporcionar información actualizada. Idealmente, las respuestas originales se actualizarían con información actual como C # 4.0. Creo que eso es lo que los chicos SO tenían originalmente en mente, una mentalidad Wiki, pero todos tienen demasiado miedo de editar la respuesta de otra persona.
Rowan
Algo interesante que he descubierto con WebServices es que (ciertamente en el proyecto que estoy probando) todos los valores bool en una cadena de consulta son opcionales por defecto. Obviamente, por defecto son FALSO.
Carlos P
44
Vale la pena señalar que los parámetros opcionales deben colocarse después de todos los parámetros no opcionales. es decir, no puede tener vacío público SomeMethod (int b = 0, int a)
Andrew Meservy
129

Otra opción es usar la palabra clave params

public void DoSomething(params object[] theObjects)
{
  foreach(object o in theObjects)
  {
    // Something with the Objects…
  }
}

Llamado como ...

DoSomething(this, that, theOther);
Tim Jarvis
fuente
57
Esta respuesta explica la palabra clave params beautifilly en 10 líneas, MSDN aún no puede hacerlo en 30. +1
Usuario2400
1
¡Gracias! Esto me inspiró a escribir una función de registro simple (para mi caso de uso actual): public void log (params object[] args){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.Length; i++){ sb.Append("{"); sb.Append(i.ToString()); sb.Append("}"); sb.Append(" "); } String.Format(sb.ToString(),args).Dump(); }Llamada de muestra:log("...Done,",(watch.ElapsedMilliseconds/1000).ToString(),"s");
pfonseca
¿podría mencionar, net 3.5 lo admite? los documentos oficiales no mencionaron eso.
T.Todua
@ T.Todua: no estoy 100% seguro de lo que está preguntando aquí: dotnet framework 3.5 incluyó C # v3.0 - la versión 3.0 admitía la palabra clave params, pero no tenía parámetros opcionales, que se introdujo en C # v4.0 , que se incluyó en dotnet framework 4.0
Tim Jarvis
@TimJarvis sí, eso era lo que estaba pidiendo. TNX
T.Todua
77

En C #, normalmente usaría múltiples formas del método:

void GetFooBar(int a) { int defaultBValue;  GetFooBar(a, defaultBValue); }
void GetFooBar(int a, int b)
{
 // whatever here
}

ACTUALIZACIÓN: Esto mencionado anteriormente fue la forma en que hice valores predeterminados con C # 2.0. Los proyectos en los que estoy trabajando ahora usan C # 4.0, que ahora admite directamente parámetros opcionales. Aquí hay un ejemplo que acabo de usar en mi propio código:

public EDIDocument ApplyEDIEnvelop(EDIVanInfo sender, 
                                   EDIVanInfo receiver, 
                                   EDIDocumentInfo info,
                                   EDIDocumentType type 
                                     = new EDIDocumentType(EDIDocTypes.X12_814),
                                   bool Production = false)
{
   // My code is here
}
stephenbayer
fuente
1
Creo que no se puede llamar a los constructores en los parámetros predeterminados, necesitan ser compatibles con "tiempo de compilación", no "tiempo de ejecución" ... ¿O me equivoco?
Alex
45

De este sitio:

http://www.tek-tips.com/viewthread.cfm?qid=1500861&page=1

C # permite el uso del atributo [Opcional] (de VB, aunque no es funcional en C #). Entonces puedes tener un método como este:

using System.Runtime.InteropServices;
public void Foo(int a, int b, [Optional] int c)
{
  ...
}

En nuestro contenedor API, detectamos parámetros opcionales (ParameterInfo p.IsOptional) y establecemos un valor predeterminado. El objetivo es marcar los parámetros como opcionales sin recurrir a kludges como tener "opcional" en el nombre del parámetro.

Kalid
fuente
44
¿Pero cómo usar la función Foo entonces? Foo (1,1); foo (1,1, nulo) y foo (1,1, Missing.value) lanzarán excepciones
Bolu
2
Extraño, algunas respuestas anteriores son mucho mejores que esto.
Maidot
26

Podría usar el método de sobrecarga ...

GetFooBar ()
GetFooBar (int a)
GetFooBar (int a, int b)

Depende de las firmas del método, en el ejemplo que proporcioné falta el método único "int b" porque tendría la misma firma que el método "int a".

Puede usar tipos anulables ...

GetFooBar (int? A, int? B)

Luego puede verificar, utilizando a.HasValue, para ver si se ha establecido un parámetro.

Otra opción sería utilizar un parámetro 'params'.

GetFooBar (objeto params [] args)

Si quisieras usar parámetros con nombre, necesitarías crear un tipo para manejarlos, aunque creo que ya hay algo así para las aplicaciones web.

Kepboy
fuente
24

Puede usar parámetros opcionales en C # 4.0 sin preocupaciones. Si tenemos un método como:

int MyMetod(int param1, int param2, int param3=10, int param4=20){....}

cuando llama al método, puede omitir parámetros como este:

int variab = MyMethod(param3:50; param1:10);

C # 4.0 implementa una función llamada "parámetros con nombre", en realidad puede pasar los parámetros por sus nombres y, por supuesto, puede pasar los parámetros en el orden que desee :)

kristi_io
fuente
20

Hola mundo opcional

Si desea que el tiempo de ejecución proporcione un valor de parámetro predeterminado, debe usar la reflexión para realizar la llamada. No es tan bueno como las otras sugerencias para esta pregunta, pero es compatible con VB.NET.

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace ConsoleApplication1
{
    class Class1
    {
        public static void sayHelloTo(
            [Optional,
            DefaultParameterValue("world")] string whom)
        {
            Console.WriteLine("Hello " + whom);
        }

        [STAThread]
        static void Main(string[] args)
        {
            MethodInfo mi = typeof(Class1).GetMethod("sayHelloTo");
            mi.Invoke(null, new Object[] { Missing.Value });
        }
    }
}
Hugh Allen
fuente
16

Una manera fácil que le permite omitir cualquier parámetro en cualquier posición es aprovechar los tipos anulables de la siguiente manera:

public void PrintValues(int? a = null, int? b = null, float? c = null, string s = "")
{
    if(a.HasValue)
        Console.Write(a);
    else
        Console.Write("-");

    if(b.HasValue)
        Console.Write(b);
    else
        Console.Write("-");

    if(c.HasValue)
        Console.Write(c);
    else
        Console.Write("-");

    if(string.IsNullOrEmpty(s)) // Different check for strings
        Console.Write(s);
    else
        Console.Write("-");
}

Las cadenas ya son tipos anulables, por lo que no necesitan el ? .

Una vez que tenga este método, las siguientes llamadas son todas válidas :

PrintValues (1, 2, 2.2f);
PrintValues (1, c: 1.2f);
PrintValues(b:100);
PrintValues (c: 1.2f, s: "hello");
PrintValues();

Cuando define un método de esa manera, tiene la libertad de establecer solo los parámetros que desea nombrándolos . Consulte el siguiente enlace para obtener más información sobre parámetros con nombre y opcionales:

Argumentos nombrados y opcionales (Guía de programación de C #) @ MSDN

Filete
fuente
9

Estoy de acuerdo con stephenbayer. Pero dado que es un servicio web, es más fácil para el usuario final usar solo una forma del método web, que usar múltiples versiones del mismo método. Creo que en esta situación, los tipos anulables son perfectos para parámetros opcionales.

public void Foo(int a, int b, int? c)
{
  if(c.HasValue)
  {
    // do something with a,b and c
  }
  else
  {
    // do something with a and b only
  }  
}
Vivek
fuente
+1 Palabra de consejo sin embargo. No haga de esto un hábito, ya que puede ser muy complicado.
mhenrixon
7

Los parámetros opcionales son para los métodos. si necesita argumentos opcionales para una clase y usted es:

  • usando c # 4.0: use argumentos opcionales en el constructor de la clase, una solución que prefiero, ya que está más cerca de lo que se hace con los métodos, por lo que es más fácil de recordar. Aquí hay un ejemplo:

    class myClass
    {
        public myClass(int myInt = 1, string myString =
                               "wow, this is cool: i can have a default string")
        {
            // do something here if needed
        }
    }
  • usando versiones de c # anteriores a c # 4.0: debe usar el encadenamiento de constructor (usando: esta palabra clave), donde los constructores más simples conducen a un "constructor maestro". ejemplo:

    class myClass
    {
        public myClass()
        {
        // this is the default constructor
        }
    
        public myClass(int myInt)
            : this(myInt, "whatever")
        {
            // do something here if needed
        }
        public myClass(string myString)
            : this(0, myString)
        {
            // do something here if needed
        }
        public myClass(int myInt, string myString)
        {
            // do something here if needed - this is the master constructor
        }
    }
baskinhu
fuente
3

La forma típica en que esto se maneja en C # como Stephen mencionó es sobrecargar el método. Al crear múltiples versiones del método con diferentes parámetros, efectivamente crea parámetros opcionales. En los formularios con menos parámetros, normalmente llamaría a la forma del método con todos los parámetros configurando sus valores predeterminados en la llamada a ese método.

cfbarbero
fuente
2

Puedes sobrecargar tu método. Un método contiene un parámetro GetFooBar(int a)y el otro contiene ambos parámetros,GetFooBar(int a, int b)

usuario2933082
fuente
2

Usar sobrecargas o usar C # 4.0 o superior

 private void GetVal(string sName, int sRoll)
 {
   if (sRoll > 0)
   {
    // do some work
   }
 }

 private void GetVal(string sName)
 {
    GetVal("testing", 0);
 }

fuente
1

Para un mayor número de parámetros opcionales, se puede usar un solo parámetro de Diccionario con el método ContainsKey. Me gusta este enfoque porque me permite pasar una Lista o una T individualmente sin tener que crear otro método completo (bueno si los parámetros se van a usar como filtros, por ejemplo).

Ejemplo (se pasaría un nuevo diccionario <string, Object> () si no se desean parámetros opcionales):

public bool Method(string ParamA, Dictionary<string,Object> AddlParams) {
    if(ParamA == "Alpha" && (AddlParams.ContainsKey("foo") || AddlParams.ContainsKey("bar"))) {
        return true;
    } else {
        return false;
    }
}
Ryan
fuente
0

En lugar de los parámetros predeterminados, ¿por qué no simplemente construir una clase de diccionario a partir de la cadena de consulta aprobada? Una implementación que es casi idéntica a la forma en que los formularios asp.net funcionan con cadenas de consulta.

es decir, Request.QueryString ["a"]

Esto desacoplará la clase de hoja del código de fábrica / repetitivo.


También es posible que desee consultar los servicios web con ASP.NET . Los servicios web son una API web generada automáticamente a través de atributos en clases de C #.

Robert Paulson
fuente
0

Llegué un poco tarde a la fiesta, pero estaba buscando la respuesta a esta pregunta y finalmente descubrí otra forma de hacerlo. Declare que los tipos de datos para los argumentos opcionales de su método web son de tipo XmlNode. Si se omite la arg opcional, se establecerá en nulo, y si está presente, puede obtener su valor de cadena llamando a arg.Value, es decir,

[WebMethod]
public string Foo(string arg1, XmlNode optarg2)
{
    string arg2 = "";
    if (optarg2 != null)
    {
        arg2 = optarg2.Value;
    }
    ... etc
}

Lo que también es decente acerca de este enfoque es que la página de inicio generada por .NET para ws todavía muestra la lista de argumentos (aunque se pierden los prácticos cuadros de entrada de texto para las pruebas).

Ron K
fuente
3
¿Es esto mejor que usar tipos anulables?
Kirk Broadhurst
0

Tengo un servicio web para escribir que toma 7 parámetros. Cada uno es un atributo de consulta opcional para una instrucción sql envuelta por este servicio web. Entonces, me vienen a la mente dos soluciones alternativas a los parámetros no opcionales ... ambos bastante pobres:

método1 (param1, param2, param 3, param 4, param 5, param 6, param7) método1 (param1, param2, param3, param 4, param5, param 6) método 1 (param1, param2, param3, param4, param5, param7 ) ... comienza a ver la imagen. De esta manera se encuentra la locura. Demasiadas combinaciones.

Ahora, para una forma más simple que parece incómoda pero que debería funcionar: método1 (param1, bool useParam1, param2, bool useParam2, etc.)

Esa es una llamada al método, se requieren valores para todos los parámetros y manejará cada caso dentro de él. También está claro cómo usarlo desde la interfaz.

Es un truco, pero funcionará.

Spanky
fuente
2
Es por eso que existen parámetros anulables.
Kirk Broadhurst
0

Tuve que hacer esto en un servicio web VB.Net 2.0. Terminé especificando los parámetros como cadenas, luego los convertí a lo que necesitaba. Se especificó un parámetro opcional con una cadena vacía. No es la solución más limpia, pero funcionó. Solo tenga cuidado de detectar todas las excepciones que pueden ocurrir.

Mate
fuente
0

Por si acaso si alguien quiere pasar una devolución de llamada (o delegate) como parámetro opcional, puede hacerlo de esta manera.

Parámetro de devolución de llamada opcional:

public static bool IsOnlyOneElement(this IList lst, Action callbackOnTrue = (Action)((null)), Action callbackOnFalse = (Action)((null)))
{
    var isOnlyOne = lst.Count == 1;
    if (isOnlyOne && callbackOnTrue != null) callbackOnTrue();
    if (!isOnlyOne && callbackOnFalse != null) callbackOnFalse();
    return isOnlyOne;
}
CodeArtist
fuente
0

¡Los parámetros opcionales no son más que parámetros predeterminados! Le sugiero que les dé los dos parámetros predeterminados. GetFooBar (int a = 0, int b = 0) si no tiene ningún método sobrecargado, dará como resultado a = 0, b = 0 si no pasa ningún valor, si pasa 1 valor, dará como resultado , pasó el valor para a, 0 y si pasa 2 valores, el primero se asignará a ay el segundo a b.

Espero que responda tu pregunta.

usuario3555836
fuente
0

En el caso de que los valores predeterminados no estén disponibles, la forma de agregar un parámetro opcional es usar la clase .NET OpcionalAtributo - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ? view = netframework-4.8

A continuación se muestra un ejemplo del código:

namespace OptionalParameterWithOptionalAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            //Calling the helper method Hello only with required parameters
            Hello("Vardenis", "Pavardenis");
            //Calling the helper method Hello with required and optional parameters
            Hello("Vardenis", "Pavardenis", "Palanga");
        }
        public static void Hello(string firstName, string secondName, 
            [System.Runtime.InteropServices.OptionalAttribute] string  fromCity)
        {
            string result = firstName + " " + secondName;
            if (fromCity != null)
            {
                result += " from " + fromCity;
            }
            Console.WriteLine("Hello " + result);
        }

    }
}
Sharunas Bielskis
fuente
-4

Puedes probar esto también
Tipo 1
public void YourMethod(int a=0, int b = 0) { //some code }


Tipo 2
public void YourMethod(int? a, int? b) { //some code }

Ankit Panwar
fuente