Wie kann ein out-Argument explizit verworfen werden?


99

Ich rufe an:

myResult = MakeMyCall(inputParams, out messages);

aber die Nachrichten interessieren mich eigentlich nicht. Wenn es ein Eingabeparameter wäre, der mir egal wäre, würde ich einfach eine Null übergeben. Wenn es die Rückkehr wäre, die mir egal wäre, würde ich sie einfach weglassen.

Gibt es eine Möglichkeit, etwas Ähnliches mit einem Out zu tun, oder muss ich eine Variable deklarieren, die ich dann ignoriere?




Vielen Dank! Schade, dass es nicht in der neuesten Version ist.
Andrew Ducker

Antworten:


99

Ab C # 7.0 kann vermieden werden, dass Parameter vorab deklariert und ignoriert werden.

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

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


1
Leider ist dies nicht in C # 7 enthalten (Stand April 2017).
Tia

2
@tia. Ich habe meine Antwort aktualisiert. Anscheinend wurde das Platzhalterzeichen von *auf geändert _. Entschuldigung, es hat so lange gedauert.
Nolonar

14
Sie hätten an ihrer Idee out voidfesthalten sollen, sie für die Syntax zu verwenden, und der Unterstrich scheint eine seltsame Wahl zu sein.
David Anderson

1
@ DavidAnderson-DCOM Obwohl ich kein Fan von Unterstrichen bin, denke ich, dass sie den Typnamen für die Überlastungsauflösung benötigen.
Jonathan Allen

Es sieht so aus, als würde immer noch ein Parameter erstellt, da ich die Zeile _ = {a value};nach dem Funktionsaufruf ohne Kompilierungsfehler hinzufügen kann.
Bip901

37

Sie müssen eine Variable deklarieren, die Sie dann ignorieren. Dies ist am häufigsten beim TryParse-Muster (oder TryWhatever) der Fall, wenn es zum Testen der Gültigkeit von Benutzereingaben verwendet wird (z. B. kann es als Zahl analysiert werden?), Ohne sich um den tatsächlich analysierten Wert zu kümmern.

Sie haben das Wort "dispose" in der Frage verwendet, was meiner Meinung nach nur unglücklich war. Wenn der out-Parameter jedoch von einem Typ ist, der IDisposable implementiert, sollten Sie Dispose auf jeden Fall aufrufen, es sei denn, in der Methodendokumentation wird ausdrücklich angegeben, dass der Empfang des Werts nicht konferiert Eigentum. Ich kann mich nicht erinnern, jemals eine Methode mit einem verfügbaren outParameter gesehen zu haben, also hoffe ich, dass dies nur eine unglückliche Wortwahl war.


Eher ein pragmatisches Anti-Muster
Jodrell

36

Leider müssen Sie etwas übergeben, da die Methode zum Festlegen erforderlich ist. Sie können also nicht senden, nullda die Methode, die zum Festlegen erforderlich ist, explodieren würde.

Ein Ansatz, um die Hässlichkeit zu verbergen, besteht darin, die Methode in eine andere Methode zu verpacken, die den outParameter für Sie so ausführt:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}

Dann können Sie aufrufen, Other_MakeMyCallohne an outnicht benötigten Parametern herumspielen zu müssen.


11

Wenn die ursprüngliche Funktion wie folgt deklariert ist:

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

Sie können eine Erweiterungsmethode wie folgt deklarieren:

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

Die Erweiterungsmethode verhält sich wie eine Überladung, die den Parameter out optional macht.


4

Der Visual Basic-Compiler erstellt dazu eine Dummy-Variable. C # könnte es tun, wenn Sie Microsoft davon überzeugen können, dass es eine gute Idee ist.


0

Wenn die Klasse von messagesGeräten IDisposable, sollten Sie es nicht ignorieren. Betrachten Sie etwa den folgenden Ansatz (möglicherweise nicht syntaktisch korrekt, da ich seit einiger Zeit kein C # mehr geschrieben habe):

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

Sobald außerhalb des usingBlocks, messageswird automatisch entsorgt.


1
Interessant. Aber müssen Sie die Variable nicht in der using-Anweisung initialisieren?
OregonGhost

1
@OregonGhost: Ja, das tust du. Wenn Sie den Wert der Variablen in der using-Anweisung ändern, wird immer noch der ursprüngliche Wert angezeigt.
Jon Skeet

@ JonSkeet Konnte nicht erfassen, dass immer noch der ursprüngliche Wert
Orkhan Alikhanov

@OrkhanAlikhanov: Der Compiler erstellt zu Beginn der usingAnweisung grundsätzlich eine Kopie der Variablen . Durch Ändern des Werts dieser Variablen innerhalb des Blocks wird also nicht geändert, welches Objekt entsorgt wird.
Jon Skeet

Übrigens sollte es geben out messages.
Orkhan Alikhanov

0

Sie müssen eine Variable für den Parameter out übergeben. Sie müssen die Variable nicht initialisieren, bevor Sie sie übergeben:

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

In der Regel können Sie "Nachrichten" nach dem Aufruf einfach ignorieren - es sei denn, "Nachrichten" müssen aus irgendeinem Grund entsorgt werden, z. B. durch die Verwendung begrenzter Systemressourcen. In diesem Fall sollten Sie Dispose () aufrufen:

messages.Dispose();

Wenn es möglicherweise eine erhebliche Menge an Speicher benötigt und für eine Weile im Gültigkeitsbereich bleibt, sollte es wahrscheinlich auf null gesetzt werden, wenn es sich um einen Referenztyp handelt, oder auf eine neue Standardinstanz, wenn es sich um einen Werttyp handelt, damit der Müll entsteht Sammler kann den Speicher zurückgewinnen:

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

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

0

In diesem Fall habe ich eine generische Erweiterungsmethode für ConcurrentDictionary erstellt, die keine Lösch- oder Entfernungsmethode enthält.

//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);
}

Beim Aufruf von einer Instanz von ConcurrentDictionary:

ClientList.TryRemoveIgnore(client.ClientId);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.