242 votes

C# 4.0 arguments optionnels out/ref

Est-ce que C# 4.0 autorise des arguments out ou ref optionnels?

2 votes

Eh bien, en C++, elles ont effectivement des 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 structure de retour que si le pointeur n'est pas nul. C'est un idiome qui remonte à l'utilisation de null pour les "arguments optionnels" dans les API C.

61 votes

@Ed et tout le monde: pourquoi cela n'aurait aucun sens? Si une fonction "retourne" une valeur via "out", je ne veux pas être obligé de l'accepter. Maintenant je sais que pour des raisons techniques le compilateur doit toujours passer quelque chose en, mais il n'y a aucune raison pour laquelle il ne pourrait pas simplement créer un local factice pour moi derrière mon dos.

6 votes

Peut-être que cela n'a pas de sens du point de vue de la façon dont les choses sont mises en œuvre 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 facultatifs" - interprétez cela en anglais plutôt qu'en CLR et cela devient raisonnable et, à mon avis, souhaitable.

5voto

Steve Points 244

Non, mais vous pouvez utiliser un délégué (par exemple Action) en tant qu'alternative.

Inspiré en partie par la réponse de Robin R lorsque j'étais confronté à une situation où je pensais avoir besoin d'un paramètre de sortie facultatif, j'ai plutôt utilisé un délégué Action. J'ai emprunté son code d'exemple pour le modifier pour l'utilisation de Action afin de montrer les différences et similitudes :

public string foo(string value, Action outResult = null)
{
    // .. faire quelque chose

    outResult?.Invoke(100);

    return value;
}

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

    string result;
    int optional = 0;

    // exemple : appel sans le paramètre de sortie facultatif
    result = foo (str);
    Console.WriteLine ("Le résultat était {0} sans utilisation de valeur facultative", result);

    // exemple : appel avec paramètre facultatif
    result = foo (str, x => optional = x);
    Console.WriteLine ("Le résultat était {0} avec une valeur facultative de {1}", result, optional);

    // exemple : appel avec paramètre facultatif nommé
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Le résultat était {0} avec une valeur facultative de {1}", result, optional);
}

Cela a l'avantage que la variable facultative apparaît dans le code source comme un int normal (le compilateur l'encapsule dans une classe de fermeture, au lieu que nous l'encapsulions explicitement dans une classe définie par l'utilisateur).

La variable nécessite une initialisation explicite parce que le compilateur ne peut pas supposer que l'Action sera appelée avant que l'appel de fonction ne se termine.

Ce n'est pas adapté à 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 avait besoin d'accéder à un état interne non présent dans la valeur de retour).

0voto

Mr_CSharp Points 41

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 suis pas sûr pourquoi un C# 7.0 pour .NET Core est même la réponse correcte pour ce thread lorsque l'on a demandé spécifiquement si C# 4.0 peut avoir un paramètre out optionnel. La réponse est NON!

0voto

Pour les types simples, vous pouvez le faire en utilisant du code non sécurisé, bien que ce ne soit ni idiomatique ni recommandé. Comme ceci :

// dangereux car le reste peut pointer n'importe où
// et nous pouvons effectuer une manipulation arbitraire du pointeur
public unsafe int Diviser(int x, int y, int* reste = null) {
    if (null != reste) *reste = x % y;
    return x / y;
}

Cela dit, il n'y a aucune raison théorique pour laquelle le C# ne pourrait pas éventuellement permettre quelque chose comme ci-dessus avec du code sûr, comme ci-dessous :

// sûr car le reste doit pointer vers un int valide ou vers rien
// et nous ne pouvons pas effectuer de manipulation arbitraire du pointeur
public int Diviser(int x, int y, out? int reste = null) {
    if (null != reste) *reste = x % y;
    return x / y;
}

Les choses pourraient devenir intéressantes cependant :

// le reste est un paramètre de sortie optionnel
// (vers un type de référence nullable)
public int Diviser(int x, int y, out? object? reste = null) {
    if (null != reste) *reste = 0 != y ? x % y : null;
    return x / y;
}

0voto

bleater Points 1160

La question directe a été répondue dans d'autres réponses bien notées, mais parfois il vaut la peine de considérer d'autres approches en fonction de ce que vous essayez d'atteindre.

Si vous souhaitez un paramètre facultatif pour permettre à l'appelant de demander éventuellement des données supplémentaires de votre méthode sur lesquelles baser une décision, une conception alternative consiste à déplacer cette logique de décision dans votre méthode et à permettre à l'appelant de passer facultativement une valeur pour ces critères de décision. Par exemple, voici une méthode qui détermine le point cardinal d'un vecteur, dans laquelle nous pourrions vouloir renvoyer la magnitude du vecteur afin que l'appelant puisse éventuellement décider si un seuil minimum doit être atteint avant que le jugement du point cardinal soit suffisamment éloigné de l'origine et donc de manière incontestable valide:

public enum Quadrant {
    Nord,
    Est,
    Sud,
    Ouest
}

// CODE INVALIDE AVEC MOTIF D'UTILISATION FICTIF DU PARAMÈTRE DE SORTIE "OPTIONAL"
public Quadrant GetJoystickQuadrant([optional] out magnitude)
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.Est;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.Nord;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.Sud;
    else q = Quadrant.Ouest;
    if ([optonal.isPresent(magnitude)]) magnitude = pos.Length();
    return q;
}

Dans ce cas, nous pourrions déplacer cette logique de "magnitude minimale" dans la méthode et obtenir une implémentation bien plus propre, surtout parce que le calcul de la magnitude implique une racine carrée et est donc inefficace en termes de calcul si tout ce que nous voulons faire est une comparaison de magnitudes, puisque nous pouvons le faire avec des valeurs au carré:

public enum Quadrant {
    Aucun, // Trop près de l'origine pour juger.
    Nord,
    Est,
    Sud,
    Ouest
}

public Quadrant GetJoystickQuadrant(float minimumMagnitude = 0.33f)
{
    Vector2 pos = GetJoystickPosition();
    if (minimumMagnitude > 0.0f && pos.LengthSquared() < minimumMagnitude * minimumMagnitude)
    {
        return Quadrant.Aucun;
    }
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    if (azimuth > -45.0f && azimuth <= 45.0f) return Quadrant.Est;
    else if (azimuth > 45.0f && azimuth <= 135.0f) return Quadrant.Nord;
    else if (azimuth > -135.0f && azimuth <= -45.0f) return Quadrant.Sud;
    return Quadrant.Ouest;
}

Bien sûr, cela pourrait ne pas toujours être viable. Puisque d'autres réponses mentionnent C# 7.0, si ce que vous faites vraiment est de retourner deux valeurs et de permettre à l'appelant d'ignorer facultativement l'une d'elles, l'idiomatique C# serait de renvoyer un tuple des deux valeurs et d'utiliser les Tuples de C# 7.0 avec les initialiseurs positionnels et le paramètre de rejet "_":

public (Quadrant, float) GetJoystickQuadrantAndMagnitude()
{
    Vector2 pos = GetJoystickPositionXY();
    float azimuth = Mathf.Atan2(pos.y, pos.x) * 180.0f / Mathf.PI;
    Quadrant q;
    if (azimuth > -45.0f && azimuth <= 45.0f) q = Quadrant.Est;
    else if (azimuth > 45.0f && azimuth <= 135.0f) q = Quadrant.Nord;
    else if (azimuth > -135.0f && azimuth <= -45.0f) q = Quadrant.Sud;
    else q = Quadrant.Ouest;
    return (q, pos.Length());
}

(Quadrant q, _) = GetJoystickQuadrantAndMagnitude();
if (q == Quadrant.Sud)
{
    // faire quelque chose.
}

-2voto

Aaron L. Points 86

Que diriez-vous de ceci ?

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

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

10 votes

"Vous devez toujours passer une valeur au paramètre depuis C#" ... Cela le rend obligatoire.

0 votes

C# ignore l'annotation [Facultatif]. Cela ne sert à rien.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X