Como você pode usar parâmetros opcionais em c #?

493

Nota: Essa pergunta foi feita no momento em que o C # ainda não suportava parâmetros opcionais (ou seja, antes do C # 4).

Estamos criando uma API da web gerada programaticamente a partir de uma classe C #. A classe possui método GetFooBar(int a, int b)e a API possui um método GetFooBarque utiliza parâmetros de consulta como &a=foo &b=bar.

As classes precisam suportar parâmetros opcionais, que não são suportados em C # no idioma. Qual é a melhor abordagem?

Kalid
fonte
7
Ou aguarde a liberação do C # 4.0. Parâmetros opcionais são suportados.
Micah

Respostas:

1055

Surpreendido, ninguém mencionou parâmetros opcionais do C # 4.0 que funcionam assim:

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

Edit: Eu sei que no momento em que a pergunta foi feita, o C # 4.0 não existia. Mas essa pergunta ainda ocupa a primeira posição no Google por "argumentos opcionais em C #", então pensei - essa resposta vale a pena estar aqui. Desculpa.

Alex
fonte
58
No momento em que a pergunta foi feita, o C # 4.0 não existia.
Forrest Marvez 27/07
91
Sim, esquecido isso, desculpe. Mas esta questão ainda classifica # 1 em Google para "C # argumentos opcionais" assim mesmo - este vale a pena resposta estar aqui :)
Alex
45
É bom para você por fornecer informações atualizadas. Idealmente, as respostas originais seriam atualizadas com informações atuais, como C # 4.0. Eu acredito que é isso que os caras do SO originalmente tinham em mente, uma mentalidade do Wiki, mas todo mundo tem um pouco de medo de editar a resposta de outra pessoa.
Rowan
Algo interessante que descobri com os WebServices é que (certamente no projeto que estou testando) todos os valores bool em uma string de consulta são opcionais por padrão. Obviamente, eles assumem o padrão FALSE.
Carlos P
4
Vale ressaltar que parâmetros opcionais devem ser colocados após todos os parâmetros não opcionais. ou seja, você não pode ter o público anulado SomeMethod (int b = 0, int a) #
Andrew Meservy 27/02/19
129

Outra opção é usar a palavra-chave params

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

Chamado como ...

DoSomething(this, that, theOther);
Tim Jarvis
fonte
57
Esta resposta explica o params palavra-chave beautifilly em 10 linhas, MSDN continua a não fazê-lo em 30. +1
User2400
1
Obrigado! Isso me inspirou a escrever uma função de registro simples (para o meu caso de uso atual): 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(); }Chamada de exemplo:log("...Done,",(watch.ElapsedMilliseconds/1000).ToString(),"s");
pfonseca 20/02/2015
você poderia mencionar, o net 3.5 suporta isso? documentos oficiais não mencionaram isso.
T.Todua
@ T.Todua - Não tenho 100% de certeza do que você está perguntando aqui - o dotnet framework 3.5 incluía o C # v3.0 - a versão 3.0 suportava a palavra-chave params, mas não tinha parâmetros opcionais, introduzidos no c # v4.0 , que foi incluído no dotnet framework 4.0
Tim Jarvis
@ TimJarvis sim, era isso que eu estava pedindo. tnx
T.Todua 17/02
77

Em C #, normalmente eu usaria várias formas do método:

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

UPDATE: Isso mencionado acima foi da maneira que eu fiz os valores padrão com o C # 2.0. Os projetos nos quais estou trabalhando agora estão usando o C # 4.0, que agora suporta diretamente parâmetros opcionais. Aqui está um exemplo que acabei de usar no meu próprio 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
fonte
1
Eu acredito que você não pode chamar construtores em parâmetros padrão, eles precisam "compilar em tempo de compilação", não "tempo de execução" ... Ou estou errado?
Alex
45

A partir deste site:

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

O C # permite o uso do atributo [Opcional] (do VB, embora não seja funcional no C #). Então você pode ter um método como este:

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

Em nosso wrapper de API, detectamos parâmetros opcionais (ParameterInfo p.IsOptional) e configuramos um valor padrão. O objetivo é marcar os parâmetros como opcionais, sem recorrer a kludges como ter "opcional" no nome do parâmetro.

Kalid
fonte
4
Mas como usar a função Foo então? Foo (1,1); foo (1,1, null) e foo (1,1, Missing.value) lançarão todas exceções
Bolu
2
Estranho, algumas respostas acima são muito melhores que isso.
Maidot
26

Você pode usar a sobrecarga de método ...

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

Depende das assinaturas do método, o exemplo que dei foi o único método "int b", porque ele teria a mesma assinatura do método "int a".

Você pode usar tipos anuláveis ​​...

GetFooBar (int? A, int? B)

Você pode verificar, usando a.HasValue, para ver se um parâmetro foi definido.

Outra opção seria usar um parâmetro 'params'.

GetFooBar (objeto de parâmetro [] args)

Se você quiser ir com parâmetros nomeados, precisará criar um tipo para lidar com eles, embora eu ache que já exista algo parecido com isso para aplicativos da web.

Kepboy
fonte
24

Você pode usar parâmetros opcionais no C # 4.0 sem preocupações. Se tivermos um método como:

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

Quando você chama o método, pode pular parâmetros como este:

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

O C # 4.0 implementa um recurso chamado "parâmetros nomeados", você pode realmente passar parâmetros pelos nomes deles e, é claro, pode passar parâmetros na ordem que desejar :)

kristi_io
fonte
20

Olá Mundo Opcional

Se você deseja que o tempo de execução forneça um valor de parâmetro padrão, é necessário usar a reflexão para fazer a chamada. Não é tão bom quanto as outras sugestões para esta pergunta, mas compatível com o 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
fonte
16

Uma maneira fácil que permite omitir qualquer parâmetro em qualquer posição , é aproveitar os tipos anuláveis ​​da seguinte maneira:

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("-");
}

As strings já são tipos anuláveis, portanto não precisam do ? .

Depois de usar esse método, as seguintes chamadas serão válidas :

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

Ao definir um método dessa maneira, você tem a liberdade de definir apenas os parâmetros desejados, nomeando- os. Consulte o link a seguir para obter mais informações sobre parâmetros nomeados e opcionais:

Argumentos nomeados e opcionais (Guia de Programação em C #) @ MSDN

SteakOverflow
fonte
9

Eu concordo com stephenbayer. Porém, como é um serviço da web, é mais fácil para o usuário final usar apenas uma forma do método da web, do que usar várias versões do mesmo método. Eu acho que nessa situação os Tipos Nullable são perfeitos para parâmetros opcionais.

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
fonte
+1 palavra de conselho embora. Não faça disso um hábito, pois pode ficar muito confuso.
Mhrixon
7

parâmetros opcionais são para métodos. se você precisa de argumentos opcionais para uma classe e você é:

  • using c # 4.0: use argumentos opcionais no construtor da classe, uma solução que eu prefiro, já que é mais próxima do que é feito com os métodos, mais fácil de lembrar. aqui está um exemplo:

    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 versões do c # anteriores ao c # 4.0: você deve usar o encadeamento de construtores (usando a: esta palavra-chave), onde construtores mais simples levam a um "construtor principal". exemplo:

    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
fonte
3

A maneira típica de lidar com isso em C # como stephen mencionado é sobrecarregar o método. Ao criar várias versões do método com parâmetros diferentes, você efetivamente cria parâmetros opcionais. Nos formulários com menos parâmetros, você normalmente chamaria o formulário do método, com todos os parâmetros configurando seus valores padrão na chamada para esse método.

cfbarbero
fonte
2

Você pode sobrecarregar seu método. Um método contém um parâmetro GetFooBar(int a)e o outro contém os dois parâmetros,GetFooBar(int a, int b)

user2933082
fonte
2

Usando sobrecargas ou usando C # 4.0 ou superior

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

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

fonte
1

Para um número maior de parâmetros opcionais, um único parâmetro de Dictionary pode ser usado com o método ContainsKey. Eu gosto dessa abordagem porque ela permite que eu passe uma lista ou um T individualmente sem precisar criar outro método (bom se parâmetros forem usados ​​como filtros, por exemplo).

Exemplo (o novo Dicionário <string, Object> () seria passado se nenhum parâmetro opcional fosse desejado):

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

Em vez de parâmetros padrão, por que não apenas construir uma classe de dicionário a partir da string de consulta passada .. uma implementação que é quase idêntica à maneira como os formulários do asp.net funcionam com as querystrings.

ie Request.QueryString ["a"]

Isso separará a classe da folha do código de fábrica / padrão.


Você também pode querer verificar Serviços da Web com o ASP.NET . Serviços da Web são uma API da Web gerada automaticamente por atributos em classes C #.

Robert Paulson
fonte
0

Um pouco atrasado para a festa, mas eu estava procurando a resposta para essa pergunta e, finalmente, descobri outra maneira de fazer isso. Declare que os tipos de dados para os argumentos opcionais do seu método da web sejam do tipo XmlNode. Se o arg opcional for omitido, ele será definido como nulo e, se estiver presente, você poderá obter o valor da string chamando arg.Value, ou seja,

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

O que também é decente nessa abordagem é a página inicial gerada pelo .NET para o ws ainda mostra a lista de argumentos (embora você perca as úteis caixas de entrada de texto para teste).

Ron K
fonte
3
Isso é melhor do que usar tipos anuláveis?
precisa
0

Eu tenho um serviço web para escrever que leva 7 parâmetros. Cada um é um atributo de consulta opcional para uma instrução sql agrupada por este serviço da web. Portanto, duas soluções alternativas para parâmetros não opcionais vêm à mente ... ambas muito ruins:

method1 (param1, param2, param 3, param 4, param 5, param 6, param7) method1 (param1, param2, param3, param 4, param5, param 6) method1 (param1, param2, param3, param4, param5, param7) ) ... comece a ver a foto. Dessa maneira está a loucura. Maneiras demais.

Agora, para uma maneira mais simples que parece estranha, mas deve funcionar: method1 (param1, bool useParam1, param2, bool useParam2, etc ...)

Essa é uma chamada de método, são necessários valores para todos os parâmetros e ele lidará com cada caso dentro dele. Também está claro como usá-lo na interface.

É um truque, mas vai funcionar.

Spanky
fonte
2
É por isso que existem parâmetros anuláveis.
precisa
0

Eu tive que fazer isso em um serviço da Web VB.Net 2.0. Acabei especificando os parâmetros como seqüências de caracteres e depois convertendo-os para o que eu precisasse. Um parâmetro opcional foi especificado com uma sequência vazia. Não é a solução mais limpa, mas funcionou. Apenas tome cuidado para capturar todas as exceções que possam ocorrer.

Matt
fonte
0

Por precaução, se alguém quiser passar um retorno de chamada (ou delegate) como um parâmetro opcional, poderá fazê-lo dessa maneira.

Parâmetro de retorno de chamada 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
fonte
0

parâmetros opcionais nada mais são do que parâmetros padrão! Eu sugiro que você dê os dois parâmetros padrão. GetFooBar (int a = 0, int b = 0) se você não tiver nenhum método sobrecarregado, resultará em a = 0, b = 0 se você não passar nenhum valor, se você passar um valor, resultará em , passou o valor para a, 0 e se você passar 2 valores, o primeiro será atribuído a e o segundo a b.

espero que responda sua pergunta.

user3555836
fonte
0

No caso em que os valores padrão não estão disponíveis, a maneira de adicionar um parâmetro opcional é usar a classe .NET OptionalAttribute - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ? view = netframework-4.8

Exemplo do código está abaixo:

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
fonte
-4

Você pode tentar isso também,
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
fonte