C # 4.0 optionale out / ref-Argumente

212

Erlaubt C # 4.0 optional outoder refArgumente?

Joe Daley
quelle
2
Weell, C ++ hat sie effektiv für "out" -Parameter - Sie können ein Adressargument auf null initialisieren lassen, und es ist durchaus üblich, Bibliothekscode zu schreiben, der eine solche Rückgabestruktur nur dann auffüllt, wenn der Zeiger nicht null ist. Dies ist eine Redewendung, die auf die Verwendung von null für "optionale Argumente" in C-APIs zurückgeht.
Andy Dent
53
@Ed und alle: Warum macht das keinen Sinn? Wenn eine Funktion einen Wert über "out" "zurückgibt", möchte ich nicht gezwungen werden, ihn zu akzeptieren. Jetzt weiß ich, dass der Compiler aus technischen Gründen noch etwas übergeben muss, aber es gibt keinen Grund, warum er nicht einfach hinter meinem Rücken einen Dummy-Local für mich erstellen konnte.
Roman Starkov
5
Vielleicht macht es unter dem Gesichtspunkt, wie Dinge implementiert werden oder was ein optionaler Parameter tatsächlich ist, keinen Sinn. Aber wie Romkyns sagte, wäre es wirklich schön, "optionale Argumente" zu haben - analysieren Sie dies eher auf Englisch als auf CLR und es wird vernünftig und in IMO wünschenswert.
Adam Tolley
9
C # nicht, VB.NET jedoch.
Jason
5
Dies wurde zu Tode geprügelt, aber ich kann nicht anders, als meine Unterstützung für optionale Argumente zu erwähnen. Ich habe mich ziemlich an optionale Argumente gewöhnt, indem ich einen nullStandardwert festgelegt habe ( ich komme aus PHP ) und getestet habe null, um mit dem Auffüllen des Arguments fortzufahren ( für diejenigen, die vertraut sind, denken Sie nachpreg_match() ), obwohl ich dies aus technischer Sicht derzeit verstehe unmöglich, und dass PHP und C # ziemlich unvergleichlich sind, wäre es immer noch ein " schönes " Tool zur Verfügung zu haben.
Dan Lugg

Antworten:

93

Wie bereits erwähnt, ist dies einfach nicht erlaubt und ich denke, es macht einen sehr guten Sinn. Um weitere Details hinzuzufügen, finden Sie hier ein Zitat aus der C # 4.0-Spezifikation , Abschnitt 21.1:

Formale Parameter von Konstruktoren, Methoden, Indexern und Delegattypen können als optional deklariert werden:

fester Parameter:
    Attribute opt Parameter-Modifikator opt Typ Bezeichner Standardargument opt
Standardargument:
    = Ausdruck

  • Ein fester Parameter mit einem Standardargument ist ein optionaler Parameter , während ein fester Parameter ohne Standardargument ein erforderlicher Parameter ist .
  • Ein erforderlicher Parameter kann nicht nach einem optionalen Parameter in einer formalen Parameterliste erscheinen .
  • Ein refoder outParameter kann kein Standardargument haben .
Tomas Petricek
quelle
Alternativ können Sie eine Überladung mit einem ref / out-Parameter erstellen. Ja, Sie werden zwei Funktionsdefinitionen haben, aber es wird das erreichen, wonach Sie suchen
Chad
201

Nein.

Eine Problemumgehung besteht darin, mit einer anderen Methode zu überladen, die keine out / ref-Parameter hat und nur Ihre aktuelle Methode aufruft.

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

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

Update: Wenn Sie C # 7.0 haben , können Sie Folgendes vereinfachen:

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

(Danke @Oskar / @Reiner für den Hinweis.)

Dunc
quelle
6
Gibt es eine Idee für eine elegantere Lösung als die Angabe von Temp / Dummy?
Louis Rhys
20
Was ist daran nicht elegant? Sieht für mich vollkommen anständig aus.
Neutrino
27
Vielleicht anständig, aber elegant ist definitiv in einer anderen Liga. Die Tatsache, dass es keine bessere Lösung gibt, bedeutet nicht, dass dies per Dekret auf dem neuesten Stand der Technik ist.
o0 '.
7
Warte auf @ o0 '. In C # 7.0 können Sie dies tun : return SomeMethod(out string temp). Weitere Informationen finden Sie
Oskar
7
In C # 7.0 können Sie eine temporäre schreibgeschützte Variable verwenden, wie:return SomeMethod(out _);
Reiner
64

Nein, aber eine weitere gute Alternative besteht darin, dass die Methode eine generische Vorlagenklasse für optionale Parameter wie folgt verwendet:

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

Dann können Sie es wie folgt verwenden:

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.
quelle
19
Es ist eine sehr vernünftige Lösung, aber eine Sache, die Sie beachten sollten, ist, dass der Compiler die Anforderung, dass der out-Parameter vor dem Beenden der Methode zugewiesen werden muss, nicht erzwingt.
Ken Smith
2
Ich mag es, aber wenn Sie keine neue Klasse erstellen möchten, können Sie sie simulieren, indem Sie ein einzelnes Elementarray übergeben.
zumalifeguard
30

Es gibt tatsächlich eine Möglichkeit, die von C # zugelassen wird. Dies kehrt zu C ++ zurück und verstößt eher gegen die schöne objektorientierte Struktur von C #.

VERWENDEN SIE DIESE METHODE MIT VORSICHT!

So deklarieren und schreiben Sie Ihre Funktion mit einem optionalen Parameter:

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

Rufen Sie dann die Funktion folgendermaßen auf:

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

Damit dies kompiliert werden kann, müssen Sie unsicheren Code in den Projektoptionen aktivieren. Dies ist eine wirklich hackige Lösung, die normalerweise nicht verwendet werden sollte. Wenn Sie jedoch für eine seltsame, arkane, mysteriöse, vom Management inspirierte Entscheidung WIRKLICH einen optionalen out-Parameter in C # benötigen, können Sie genau das tun.

wizard07KSU
quelle
6

ICYMI: In den hier aufgezählten neuen Funktionen für C # 7.0 ist "Verwerfen" jetzt als Out-Parameter in Form eines _ zulässig, damit Sie Parameter ignorieren können, die Sie nicht interessieren:

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

PS: Wenn Sie auch mit dem Teil "out var x" verwechselt werden, lesen Sie auch die neue Funktion zu "Out Variables" auf dem Link.

jbdeguzman
quelle
ist es _ oder * "p.GetCoordinates (out int x, out *); // Ich kümmere mich nur um x"
Onur Topal
Es sieht so aus, als hätte ich eine ältere Version des Dokuments gesucht.
Onur Topal
2

Nein, aber Sie können Actionalternativ einen Delegaten (z. B. ) verwenden.

Teilweise inspiriert von Robin Rs Antwort in einer Situation, in der ich dachte, ich wolle einen optionalen out-Parameter, habe ich stattdessen einen ActionDelegaten verwendet. Ich habe seinen Beispielcode ausgeliehen, um ihn für die Verwendung zu ändern Action<int>, um die Unterschiede und Ähnlichkeiten aufzuzeigen:

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

Dies hat den Vorteil, dass die optionale Variable in der Quelle als normales int angezeigt wird (der Compiler umschließt sie in einer Abschlussklasse, anstatt sie explizit in eine benutzerdefinierte Klasse einzuschließen).

Die Variable muss explizit initialisiert werden, da der Compiler nicht davon ausgehen kann, dass die Actionaufgerufen wird, bevor der Funktionsaufruf beendet wird.

Es ist nicht für alle Anwendungsfälle geeignet, hat aber für meinen tatsächlichen Anwendungsfall gut funktioniert (eine Funktion, die Daten für einen Komponententest bereitstellt und für die ein neuer Komponententest Zugriff auf einen internen Status benötigt, der im Rückgabewert nicht vorhanden ist).

Steve
quelle
0

Verwenden Sie eine überladene Methode ohne den Parameter out, um die Methode mit dem Parameter out für C # 6.0 und niedriger aufzurufen. Ich bin mir nicht sicher, warum ein C # 7.0 für .NET Core überhaupt die richtige Antwort für diesen Thread ist, als speziell gefragt wurde, ob C # 4.0 einen optionalen out-Parameter haben kann. Die Antwort ist nein!

Mr_CSharp
quelle
-2

Was ist damit?

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

Sie müssen noch einen Wert von C # an den Parameter übergeben, dies ist jedoch ein optionaler ref-Parameter.

Aaron L.
quelle
8
"Sie müssen noch einen Wert von C # an den Parameter übergeben" ... Dies macht es nicht optional.
Arturo Torres Sánchez
C # ignoriert die [Optional]Anmerkung. Das hilft nicht.
ToolmakerSteve
-4
void foo(ref int? n)
{
    return null;
}
user6469927
quelle
1
Bitte fügen Sie einige Erklärungen zu Ihrem Code hinzu, damit jeder das Konzept leicht versteht
Techspider
1
Obwohl dieser Code die Frage möglicherweise beantwortet, würde die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie er die Frage beantwortet, ihren langfristigen Wert erheblich verbessern. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen.
Toby Speight
2
Dies führt zu einem Syntaxfehler, da die Methode den Rückgabetyp void hat. Beantwortet auch keine Frage.
Nathan Montez