Arguments optionnels de sortie / référence C # 4.0

212

C # 4.0 autorise-t-il des arguments facultatifs outou ref?

Joe Daley
la source
2
Weell, C ++ les a effectivement pour les paramètres "out" - vous pouvez avoir un argument d'adresse initialisé à null et il est assez courant d'écrire du code de bibliothèque qui ne remplira une telle structure de retour que si le pointeur n'est pas nul. C'est un idiome qui revient à utiliser null pour les "arguments facultatifs" dans les API C.
Andy Dent
53
@Ed et tout le monde: pourquoi cela n'aurait-il aucun sens? Si une fonction "renvoie" une valeur via "out", je ne veux pas être forcé de l'accepter. Maintenant, je sais que pour des raisons techniques, le compilateur doit encore transmettre quelque chose, mais il n'y a aucune raison pour qu'il ne puisse pas simplement créer un local factice pour moi derrière mon dos.
Roman Starkov du
5
Peut-être que cela n'a aucun sens du point de vue de la façon dont les choses sont implémentées ou de ce qu'est réellement un paramètre facultatif. Mais comme l'a dit romkyns, ce serait vraiment bien d'avoir des "arguments de sortie optionnels" - analysez cela en anglais plutôt qu'en CLR et cela devient raisonnable et souhaitable à l'OMI.
Adam Tolley
9
C # ne le fait pas, mais VB.NET le fait.
Jason
5
Cela a été battu à mort, cependant, je ne peux pas m'empêcher de mentionner mon soutien aux arguments optionnels de sortie. Je suis devenu assez habitué aux arguments optionnels par référence en définissant un paramètre par nulldéfaut ( je viens de PHP ) et en testant pour nullcontinuer à remplir l'argument ( pour ceux qui connaissent, pensezpreg_match() ) Quoi qu'il en soit, même si je comprends d'un point de vue technique, cela peut être actuellement impossible, et que PHP et C # sont plutôt incomparables, ce serait quand même un " bel " outil à disposition.
Dan Lugg

Réponses:

93

Comme déjà mentionné, ce n'est tout simplement pas autorisé et je pense que c'est très logique. Cependant, pour ajouter plus de détails, voici une citation de la spécification C # 4.0 , section 21.1:

Les paramètres formels des constructeurs, méthodes, indexeurs et types délégués peuvent être déclarés facultatifs:

fixe paramètres:
    attributs opt paramètre modificateur opt de type identifiant par défaut argument opt
par défaut argument:
    = expression

  • Un paramètre fixe avec un argument par défaut est un paramètre facultatif , tandis qu'un paramètre fixe sans argument par défaut est un paramètre obligatoire .
  • Un paramètre obligatoire ne peut pas apparaître après un paramètre facultatif dans une liste de paramètres formels .
  • Un paramètre refou outne peut pas avoir d' argument par défaut .
Tomas Petricek
la source
Alternativement, vous pouvez créer une surcharge avec un paramètre ref / out. Oui, vous aurez deux définitions de fonctions, mais cela accomplira ce que vous recherchez
Chad
201

Non.

Une solution de contournement consiste à surcharger une autre méthode qui n'a pas de paramètres out / ref et qui appelle simplement votre méthode actuelle.

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

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

Mise à jour: si vous avez C # 7.0 , vous pouvez simplifier:

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

(Merci @Oskar / @Reiner de l'avoir signalé.)

Dunc
la source
6
une idée de solution plus élégante que de déclarer temp / dummy?
Louis Rhys
20
Qu'est-ce qui n'est pas élégant à ce sujet? Ça me semble parfaitement convenable.
Neutrino
27
Peut-être décent, mais élégant est définitivement dans une autre ligue. Le fait qu'il n'y ait pas de meilleure solution ne signifie pas qu'il s'agit d'un état de l'art par décret.
o0 '.
7
Attendez @ o0 '. En C # 7.0 , vous serez en mesure de le faire: return SomeMethod(out string temp). Voir plus ici: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar
7
En C # 7.0, vous pouvez utiliser une variable temporaire en écriture seule comme:return SomeMethod(out _);
Reiner
64

Non, mais une autre excellente alternative consiste à faire en sorte que la méthode utilise une classe de modèle générique pour les paramètres facultatifs comme suit:

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

Ensuite, vous pouvez l'utiliser comme suit:

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
la source
19
C'est une solution très raisonnable, mais une chose à savoir est que le compilateur n'appliquera pas l'exigence que le paramètre out soit assigné avant de quitter la méthode.
Ken Smith
2
Je l'aime bien, mais si vous ne voulez pas créer une nouvelle classe, vous pouvez la simuler en passant dans un seul tableau d'éléments.
zumalifeguard
30

Il existe en fait un moyen de le faire qui est autorisé par C #. Cela revient au C ++, et viole plutôt la belle structure orientée objet de C #.

UTILISEZ CETTE MÉTHODE AVEC ATTENTION!

Voici la façon dont vous déclarez et écrivez votre fonction avec un paramètre facultatif:

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.
    }
}

Appelez ensuite la fonction comme ceci:

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

Pour que cela soit compilé, vous devrez activer le code non sécurisé dans les options du projet. C'est une solution vraiment hacky qui ne devrait généralement pas être utilisée, mais si vous avez une décision étrange, mystérieuse et mystérieuse inspirée par la gestion, vous avez VRAIMENT besoin d'un paramètre de sortie facultatif en C #, cela vous permettra de faire exactement cela.

wizard07KSU
la source
6

ICYMI: Inclus dans les nouvelles fonctionnalités de C # 7.0 énumérées ici , "discards" est désormais autorisé en tant que paramètres de sortie sous la forme d'un _, pour vous permettre d'ignorer les paramètres qui ne vous intéressent pas:

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

PS si vous êtes également confondu avec la partie "out var x", lisez également la nouvelle fonctionnalité sur "Out Variables" sur le lien.

jbdeguzman
la source
est-ce _ ou * "p.GetCoordinates (out int x, out *); // Je ne me soucie que de x"
Onur Topal
on dirait que je cherchais une ancienne version du document.
Onur Topal le
2

Non, mais vous pouvez utiliser un délégué (par exemple Action) comme alternative.

Inspiré en partie par la réponse de Robin R face à une situation où je pensais que je voulais un paramètre de sortie facultatif, j'ai plutôt utilisé un Actiondélégué. J'ai emprunté son exemple de code à modifier pour l'utiliser Action<int>afin de montrer les différences et les 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);
}

Cela a l'avantage que la variable facultative apparaît dans la source comme un entier normal (le compilateur l'enveloppe dans une classe de fermeture, plutôt que nous l'enveloppons explicitement dans une classe définie par l'utilisateur).

La variable a besoin d'une initialisation explicite car le compilateur ne peut pas supposer que le Actionsera appelé avant la fin de l'appel de fonction.

Il ne convient pas à tous les cas d'utilisation, mais a bien fonctionné pour mon cas d'utilisation réel (une fonction qui fournit des données pour un test unitaire et où un nouveau test unitaire devait accéder à un état interne non présent dans la valeur de retour).

Steve
la source
0

Utilisez une méthode surchargée sans le paramètre out pour appeler celle avec le paramètre out pour C # 6.0 et inférieur. Je ne sais pas pourquoi un C # 7.0 pour .NET Core est même la bonne réponse pour ce fil quand il a été spécifiquement demandé si C # 4.0 peut avoir un paramètre de sortie facultatif. La réponse est non!

Mr_CSharp
la source
-2

Et comme ça?

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

Vous devez toujours passer une valeur au paramètre à partir de C #, mais c'est un paramètre ref facultatif.

Aaron L.
la source
8
"Vous devez toujours passer une valeur au paramètre à partir de C #" ... Cela ne le rend pas facultatif.
Arturo Torres Sánchez
C # ignore l' [Optional]annotation. Cela n'aide pas.
ToolmakerSteve
-4
void foo(ref int? n)
{
    return null;
}
user6469927
la source
1
Veuillez ajouter quelques explications sur votre code pour que tout le monde comprenne facilement le concept
techspider
1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant pourquoi et / ou comment il répond à la question améliorerait considérablement sa valeur à long terme. Veuillez modifier votre réponse pour ajouter des explications.
Toby Speight
2
Cela entraînera une erreur de syntaxe car la méthode a un type de retour vide. Aussi, ne répond pas à la question.
Nathan Montez