Comment écarter explicitement un argument out?


99

Je passe un appel:

myResult = MakeMyCall(inputParams, out messages);

mais je ne me soucie pas vraiment des messages. Si c'était un paramètre d'entrée dont je ne me souciais pas, je passerais simplement un null. Si c'était le retour, je m'en fichais, je le laisserais simplement.

Existe-t-il un moyen de faire quelque chose de similaire avec un out, ou dois-je déclarer une variable que j'ignorerai ensuite?




Merci! Dommage que ce ne soit pas dans la dernière version.
Andrew Ducker

Réponses:


99

À partir de C # 7.0, il est possible d'éviter de pré-déclarer des paramètres et de les ignorer.

public void PrintCoordinates(Point p)
{
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
}

public void PrintXCoordinate(Point p)
{
    p.GetCoordinates(out int x, out _); // I only care about x
    WriteLine($"{x}");
}

Source: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/


1
Malheureusement, cela n'est pas inclus dans C # 7 (à partir d'avril 2017).
tia

2
@tia. J'ai mis à jour ma réponse. Apparemment, le caractère générique est passé de *à _. Désolé, cela a pris si longtemps.
Nolonar

14
Ils auraient dû s'en tenir à leur idée à utiliser out voidpour la syntaxe, et le trait de soulignement semble être un choix étrange.
David Anderson

1
@ DavidAnderson-DCOM Bien que je ne sois pas fan du trait de soulignement, je pense qu'ils ont besoin du nom de type pour la résolution de surcharge.
Jonathan Allen

Il semble que cela crée toujours un paramètre, car je peux ajouter la ligne _ = {a value};après l'appel de fonction sans aucune erreur de compilation.
Bip901

37

Malheureusement, vous devez passer quelque chose car la méthode est requise pour le définir. Vous ne pouvez donc pas envoyer nullcar la méthode, étant obligée de la définir, exploserait.

Une approche pour masquer la laideur serait d'envelopper la méthode dans une autre méthode qui fait le outparamètre pour vous comme ceci:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}

Ensuite, vous pouvez appeler Other_MakeMyCallsans avoir à manipuler les outparamètres dont vous n'avez pas besoin.


37

Vous devez déclarer une variable que vous ignorerez ensuite. C'est le plus souvent le cas avec le modèle TryParse (ou TryWwhat), lorsqu'il est utilisé pour tester la validité de l'entrée utilisateur (par exemple, peut-elle être analysée comme un nombre?) Sans se soucier de la valeur analysée réelle.

Vous avez utilisé le mot «disposer» dans la question, ce que je soupçonne était juste malheureux - mais si le paramètre out est d'un type qui implémente IDisposable, vous devez certainement appeler Dispose à moins que la documentation de la méthode indique explicitement que la réception de la valeur ne confère pas la possession. Je ne me souviens pas avoir jamais vu une méthode avec un outparamètre jetable , alors j'espère que ce n'était qu'un choix de mots malchanceux.


Plus d'un anti-motif pragmatique
Jodrell

11

Si la fonction d'origine est déclarée comme ceci:

class C
{
    public Result MakeMyCall(Object arg, out List<String> messages);
}

Vous pouvez déclarer une méthode d'extension comme celle-ci:

static class CExtension
{
    public static Result MakeMyCall(this C obj, Object arg)
    {
        List<String> unused;
        return obj.MakeMyCall(arg, out unused);
    }
}

La méthode d'extension se comportera comme une surcharge qui rend le paramètre out facultatif.


4

Le compilateur Visual Basic effectue cela en créant une variable factice. C # pourrait le faire, si vous pouvez convaincre Microsoft que c'est une bonne idée.


0

Si la classe des messagesoutils IDisposable, vous ne devriez pas l'ignorer. Considérez quelque chose comme l'approche suivante (peut ne pas être syntaxiquement correcte car je n'ai pas écrit C # depuis un moment):

using (FooClass messages) {
    myResult = MakeMyCall(inputParams, messages);
}

Une fois en dehors du usingbloc, messagessera éliminé automatiquement.


1
Intéressant. Mais n'avez-vous pas besoin d'initialiser la variable dans l'instruction using?
OregonGhost

1
@OregonGhost: Oui, c'est vrai. Et si vous modifiez la valeur de la variable dans l'instruction using, c'est toujours la valeur d' origine qui est supprimée.
Jon Skeet

@JonSkeet Impossible de saisir que c'est toujours la valeur d'origine qui est supprimée.
Orkhan Alikhanov

@OrkhanAlikhanov: Le compilateur prend essentiellement une copie de la variable au début de l' usinginstruction. Donc, changer la valeur de cette variable dans le bloc ne change pas quel objet est supprimé.
Jon Skeet

Au fait, il devrait y en avoir out messages.
Orkhan Alikhanov

0

Vous devez passer une variable pour le paramètre out. Il n'est pas nécessaire d'initialiser la variable avant de la transmettre:

MyMessagesType messages;
myResult = MakeMyCall(inputParams, out messages); 

En règle générale, vous pouvez simplement ignorer les `` messages '' après l'appel - à moins que les `` messages '' aient besoin d'être supprimés pour une raison quelconque, telle que l'utilisation de ressources système limitées, auquel cas vous devez appeler Dispose ():

messages.Dispose();

S'il peut utiliser une quantité importante de mémoire et qu'il va rester dans la portée pendant un certain temps, il devrait probablement être défini sur null s'il s'agit d'un type de référence ou sur une nouvelle instance par défaut s'il s'agit d'un type valeur, de sorte que le garbage le collecteur peut récupérer la mémoire:

messages = null; // Allow GC to reclaim memory for reference type.

messages = new MyMessageType(); // Allow GC to reclaim memory for value type.

0

Dans ce cas, j'ai créé une méthode d'extension générique pour ConcurrentDictionary qui n'a pas de méthode Delete ou Remove.

//Remove item from list and ignore reference to removed item
public static void TryRemoveIgnore<K,T>(this ConcurrentDictionary<K,T> dictionary, K key)
{
    T CompletelyIgnored;
    dictionary.TryRemove(key, out CompletelyIgnored);
}

Lorsqu'il est appelé à partir d'une instance de ConcurrentDictionary:

ClientList.TryRemoveIgnore(client.ClientId);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.