Comment pouvez-vous utiliser des paramètres facultatifs en C #?

493

Remarque: Cette question a été posée à un moment où C # ne supportait pas encore les paramètres facultatifs (c'est-à-dire avant C # 4).

Nous créons une API Web générée par programme à partir d'une classe C #. La classe a une méthode GetFooBar(int a, int b)et l'API a une méthode GetFooBarprenant des paramètres de requête comme &a=foo &b=bar.

Les classes doivent prendre en charge les paramètres facultatifs, ce qui n'est pas pris en charge en langage C #. Quelle est la meilleure approche?

Kalid
la source
7
Ou attendez que l'unité jusqu'à ce que C # 4.0 soit publiée. Les paramètres facultatifs sont pris en charge.
Micah

Réponses:

1055

Surpris, personne n'a mentionné les paramètres facultatifs C # 4.0 qui fonctionnent comme ceci:

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

Edit: Je sais qu'au moment où la question a été posée, C # 4.0 n'existait pas. Mais cette question se classe toujours n ° 1 dans Google pour les "arguments facultatifs C #", alors j'ai pensé - cette réponse mérite d'être ici. Désolé.

Alex
la source
58
Au moment où la question a été posée, C # 4.0 n'existait pas.
Forrest Marvez
91
Oui, j'ai oublié ça, désolé. Mais cette question se classe toujours n ° 1 dans Google pour les "arguments facultatifs C #", de toute façon - cette réponse vaut la peine d'être ici :)
Alex
45
Bon pour vous pour fournir des informations à jour. Idéalement, les réponses originales seraient mises à jour avec des informations actuelles telles que C # 4.0. Je crois que c'est ce que les gars de SO avaient à l'esprit à l'origine, une mentalité Wiki, mais tout le monde a un peu trop peur de modifier la réponse de quelqu'un d'autre.
Rowan
Quelque chose d'intéressant que j'ai découvert avec WebServices est que (certainement dans le projet que je teste) toutes les valeurs booléennes d'une chaîne de requête sont facultatives par défaut. De toute évidence, ils sont par défaut FAUX.
Carlos P
4
Il convient de noter que les paramètres facultatifs doivent être placés après tous les paramètres non facultatifs. c'est-à-dire que vous ne pouvez pas avoir public null SomeMethod (int b = 0, int a)
Andrew Meservy
129

Une autre option consiste à utiliser le mot clé params

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

Appelé comme ...

DoSomething(this, that, theOther);
Tim Jarvis
la source
57
Cette réponse explique le mot-clé params beautifilly en 10 lignes, MSDN ne parvient toujours pas à le faire en 30. +1
User2400
1
Merci! Cela m'a inspiré pour écrire une fonction de journalisation simple (pour mon cas d'utilisation actuel): 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(); }Exemple d'appel:log("...Done,",(watch.ElapsedMilliseconds/1000).ToString(),"s");
pfonseca
pourriez-vous mentionner, Net 3.5 le prend-il en charge? les documents officiels ne l'ont pas mentionné.
T.Todua
@ T.Todua - Je ne suis pas sûr à 100% de ce que vous demandez ici - le framework dotnet 3.5 inclus C # v3.0 - la version 3.0 supportait le mot-clé params, mais n'avait pas de paramètres optionnels, qui a été introduit dans C # v4.0 , qui a été inclus dans le framework dotnet 4.0
Tim Jarvis
@ TimJarvis oui, c'est ce que je demandais. tnx
T.Todua
77

En C #, j'utiliserais normalement plusieurs formes de la méthode:

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

MISE À JOUR: Cela mentionné ci-dessus était la façon dont j'ai fait les valeurs par défaut avec C # 2.0. Les projets sur lesquels je travaille actuellement utilisent C # 4.0 qui prend désormais directement en charge les paramètres facultatifs. Voici un exemple que je viens d'utiliser dans mon propre code:

public EDIDocument ApplyEDIEnvelop(EDIVanInfo sender, 
                                   EDIVanInfo receiver, 
                                   EDIDocumentInfo info,
                                   EDIDocumentType type 
                                     = new EDIDocumentType(EDIDocTypes.X12_814),
                                   bool Production = false)
{
   // My code is here
}
stephenbayer
la source
1
Je crois que vous ne pouvez pas appeler les constructeurs dans les paramètres par défaut, ils doivent être compatibles "au moment de la compilation", pas au "moment de l'exécution" ... Ou je me trompe?
Alex
45

De ce site:

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

C # autorise l'utilisation de l'attribut [Facultatif] (de VB, mais non fonctionnel en C #). Vous pouvez donc avoir une méthode comme celle-ci:

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

Dans notre wrapper API, nous détectons les paramètres facultatifs (ParameterInfo p.IsOptional) et définissons une valeur par défaut. Le but est de marquer les paramètres comme optionnels sans recourir à des contraintes comme avoir "optionnel" dans le nom du paramètre.

Kalid
la source
4
Mais comment utiliser la fonction Foo alors? Foo (1,1); foo (1,1, null) et foo (1,1, Missing.value) lèveront tous des exceptions
Bolu
2
Étrange, certaines réponses ci-dessus sont bien meilleures que cela.
Maidot
26

Vous pouvez utiliser la surcharge de méthode ...

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

Cela dépend des signatures de méthode, l'exemple que j'ai donné manque la méthode "int b" uniquement car elle aurait la même signature que la méthode "int a".

Vous pouvez utiliser des types Nullable ...

GetFooBar (int? A, int? B)

Vous pouvez ensuite vérifier, à l'aide de a.HasValue, pour voir si un paramètre a été défini.

Une autre option serait d'utiliser un paramètre «params».

GetFooBar (objet params [] args)

Si vous vouliez utiliser des paramètres nommés, il faudrait créer un type pour les gérer, bien que je pense qu'il existe déjà quelque chose comme ça pour les applications Web.

Kepboy
la source
24

Vous pouvez utiliser des paramètres facultatifs en C # 4.0 sans aucun souci. Si nous avons une méthode comme:

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

lorsque vous appelez la méthode, vous pouvez ignorer des paramètres comme celui-ci:

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

C # 4.0 implémente une fonctionnalité appelée "paramètres nommés", vous pouvez réellement passer les paramètres par leurs noms, et bien sûr vous pouvez passer les paramètres dans l'ordre que vous voulez :)

kristi_io
la source
20

Bonjour le monde facultatif

Si vous souhaitez que le runtime fournisse une valeur de paramètre par défaut, vous devez utiliser la réflexion pour effectuer l'appel. Pas aussi sympa que les autres suggestions pour cette question, mais compatible avec 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
la source
16

Un moyen simple qui vous permet d'omettre tous les paramètres dans n'importe quelle position , tire parti des types nullables comme suit:

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

Les chaînes sont déjà de type nullable, elles n'ont donc pas besoin du ?.

Une fois que vous avez cette méthode, les appels suivants sont tous valides :

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

Lorsque vous définissez une méthode de cette manière, vous avez la liberté de définir uniquement les paramètres souhaités en les nommant . Voir le lien suivant pour plus d'informations sur les paramètres nommés et facultatifs:

Arguments nommés et facultatifs (Guide de programmation C #) @ MSDN

SteakOverflow
la source
9

Je suis d'accord avec stephenbayer. Mais comme il s'agit d'un service Web, il est plus facile pour l'utilisateur final d'utiliser une seule forme de la méthode Web, que d'utiliser plusieurs versions de la même méthode. Je pense que dans cette situation, les types nullables sont parfaits pour les paramètres facultatifs.

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
la source
+1 Un conseil cependant. N'en faites pas une habitude car cela peut devenir très salissant.
mhenrixon
7

les paramètres facultatifs concernent les méthodes. si vous avez besoin d'arguments facultatifs pour une classe et que vous êtes:

  • en utilisant c # 4.0: utilisez des arguments optionnels dans le constructeur de la classe, une solution que je préfère, car elle est plus proche de ce qui est fait avec les méthodes, donc plus facile à retenir. voici un exemple:

    class myClass
    {
        public myClass(int myInt = 1, string myString =
                               "wow, this is cool: i can have a default string")
        {
            // do something here if needed
        }
    }
  • en utilisant des versions c # antérieures à c # 4.0: vous devez utiliser le chaînage de constructeur (en utilisant: ce mot-clé), où les constructeurs plus simples mènent à un "constructeur maître". exemple:

    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
la source
3

La manière typique dont cela est géré en C # comme stephen l'a mentionné est de surcharger la méthode. En créant plusieurs versions de la méthode avec différents paramètres, vous créez efficacement des paramètres facultatifs. Dans les formulaires avec moins de paramètres, vous appelez généralement le formulaire de la méthode avec tous les paramètres définissant vos valeurs par défaut dans l'appel à cette méthode.

cfbarbero
la source
2

Vous pouvez surcharger votre méthode. Une méthode contient un paramètre GetFooBar(int a)et l'autre contient les deux paramètres,GetFooBar(int a, int b)

user2933082
la source
2

Utiliser des surcharges ou utiliser C # 4.0 ou supérieur

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

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

la source
1

Pour un plus grand nombre de paramètres facultatifs, un seul paramètre de Dictionary peut être utilisé avec la méthode ContainsKey. J'aime cette approche car elle me permet de passer une List ou un T individuellement sans avoir à créer une toute autre méthode (sympa si les paramètres doivent être utilisés comme filtres, par exemple).

Exemple (un nouveau dictionnaire <chaîne, objet> () serait transmis si aucun paramètre facultatif n'est souhaité):

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

Au lieu des paramètres par défaut, pourquoi ne pas simplement construire une classe de dictionnaire à partir de la chaîne de requête passée .. une implémentation qui est presque identique à la façon dont les formulaires asp.net fonctionnent avec les chaînes de requête.

c'est-à-dire Request.QueryString ["a"]

Cela découplera la classe de feuille du code d'usine / passe-partout.


Vous pouvez également vouloir vérifier les services Web avec ASP.NET . Les services Web sont une API Web générée automatiquement via des attributs sur les classes C #.

Robert Paulson
la source
0

Un peu tard pour la fête, mais je cherchais la réponse à cette question et j'ai finalement trouvé une autre façon de le faire. Déclarez les types de données des arguments facultatifs de votre méthode Web comme étant XmlNode. Si l'argument facultatif est omis, il sera défini sur null, et s'il est présent, vous pouvez obtenir sa valeur de chaîne en appelant arg.Value, c'est-à-dire,

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

Ce qui est également décent dans cette approche, c'est que la page d'accueil générée par .NET pour le ws affiche toujours la liste des arguments (bien que vous perdiez les zones de saisie de texte pratiques pour les tests).

Ron K
la source
3
Est-ce mieux que d'utiliser des types nullables?
Kirk Broadhurst
0

J'ai un service web à écrire qui prend 7 paramètres. Chacun est un attribut de requête facultatif à une instruction sql enveloppée par ce service Web. Donc, deux solutions de contournement aux paramètres non facultatifs viennent à l'esprit ... les deux sont assez pauvres:

méthode1 (param1, param2, param 3, param 4, param 5, param 6, param7) méthode1 (param1, param2, param3, param 4, param5, param 6) méthode 1 (param1, param2, param3, param4, param5, param7 ) ... commencez à voir l'image. De cette façon réside la folie. Beaucoup trop de combinaisons.

Maintenant, pour un moyen plus simple qui semble maladroit mais devrait fonctionner: méthode1 (param1, bool useParam1, param2, bool useParam2, etc ...)

C'est un appel de méthode, des valeurs pour tous les paramètres sont requises et il traitera chaque cas à l'intérieur. Il est également clair comment l'utiliser à partir de l'interface.

C'est un hack, mais ça marchera.

Spanky
la source
2
C'est pourquoi des paramètres nullables existent.
Kirk Broadhurst
0

J'ai dû le faire dans un service Web VB.Net 2.0. J'ai fini par spécifier les paramètres sous forme de chaînes, puis les convertir en tout ce dont j'avais besoin. Un paramètre facultatif a été spécifié avec une chaîne vide. Pas la solution la plus propre, mais cela a fonctionné. Veillez simplement à intercepter toutes les exceptions qui peuvent se produire.

Mat
la source
0

Pour juste au cas où si quelqu'un veut passer un rappel (ou delegate) comme paramètre facultatif, peut le faire de cette façon.

Paramètre de rappel facultatif:

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
la source
0

les paramètres optionnels ne sont que des paramètres par défaut! je vous suggère de leur donner les deux paramètres par défaut. GetFooBar (int a = 0, int b = 0) si vous n'avez pas de méthode surchargée, résultera en a = 0, b = 0 si vous ne transmettez aucune valeur, si vous transmettez 1 valeur, résultera en , a passé la valeur pour a, 0 et si vous passez 2 valeurs, la première sera affectée à a et la seconde à b.

j'espère que cela répond à votre question.

user3555836
la source
0

Dans le cas où les valeurs par défaut ne sont pas disponibles, la façon d'ajouter un paramètre facultatif consiste à utiliser la classe .NET OptionalAttribute - https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.optionalattribute ? view = netframework-4.8

Un exemple de code est ci-dessous:

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
la source
-4

Vous pouvez également essayer ceci
Type 1
public void YourMethod(int a=0, int b = 0) { //some code }


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

Ankit Panwar
la source