Argomenti opzionali out / ref di C # 4.0


212

C # 4.0 consente facoltativi outo refargomenti?


2
Weell, C ++ li ha effettivamente per i parametri "out" - puoi avere un argomento address inizializzato su null ed è abbastanza comune scrivere un codice di libreria che popolerà tale struttura di ritorno solo se il puntatore è diverso da null. Questo è un linguaggio che risale all'utilizzo di null per "argomenti opzionali" nelle API C.
Andy Dent,

53
@Ed e tutti: perché questo non avrebbe senso? Se una funzione "restituisce" un valore tramite "out", non voglio essere costretto ad accettarlo. Ora so che per ragioni tecniche il compilatore deve ancora passare qualcosa, ma non c'è motivo per cui non possa semplicemente creare un locale fittizio per me alle mie spalle.
Roman Starkov,

5
Forse non ha senso dal punto di vista dell'implementazione o di un parametro opzionale. Ma come ha detto Romkyns, sarebbe davvero bello avere "argomenti opzionali" - analizzalo in inglese piuttosto che in CLR e diventa ragionevole e in IMO desiderabile.
Adam Tolley,

9
C # no, ma VB.NET lo fa.
Jason,

5
Questo è stato picchiato a morte, tuttavia, non posso fare a meno di menzionare il mio supporto per argomenti opzionali. Mi sono abituato abbastanza agli argomenti opzionali come riferimento impostando un nulldefault ( vengo da PHP ) e testando per nullprocedere con il popolamento dell'argomento ( per quelli familiari, pensapreg_match() ) Comunque, mentre capisco da un punto di vista tecnico questo potrebbe essere attualmente impossibile, e che PHP e C # sono piuttosto incomparabili, sarebbe comunque uno strumento " carino " da avere a disposizione.
Dan Lugg,

Risposte:


93

Come già accennato, semplicemente non è permesso e penso che abbia un buon senso. Tuttavia, per aggiungere ulteriori dettagli, ecco un preventivo dalle specifiche C # 4.0 , sezione 21.1:

I parametri formali di costruttori, metodi, indicizzatori e tipi di delegati possono essere dichiarati facoltativi:

-parametro fisso:
    attributi opt parametro-modificatore di opt tipo identificativo default-argomento opt
default-argomento:
    = espressione

  • Un parametro fisso con un argomento predefinito è un parametro facoltativo , mentre un parametro fisso senza un argomento predefinito è un parametro obbligatorio .
  • Un parametro richiesto non può apparire dopo un parametro facoltativo in un elenco di parametri formale .
  • Un parametro refo outnon può avere un argomento predefinito .

In alternativa, è possibile creare un sovraccarico con un parametro ref / out. Sì, avrai due definizioni di funzione, ma realizzerà ciò che stai cercando
Ciad

201

No.

Una soluzione alternativa consiste nel sovraccaricare con un altro metodo che non ha parametri out / ref e che chiama semplicemente il metodo corrente.

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

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

Aggiornamento: se si dispone di C # 7.0 , è possibile semplificare:

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

(Grazie @Oskar / @Reiner per averlo segnalato.)


6
qualche idea per una soluzione più elegante di dichiarare temp / dummy?
Louis Rhys,

20
Cosa non è elegante in questo? Mi sembra perfettamente decente.
Neutrino,

27
Forse decente, ma elegante è sicuramente in un'altra lega. Il fatto che non esista una soluzione migliore non significa che questo sia all'avanguardia per decreto.
o0 '.

7
Aspetta @ o0 '. In C # 7.0 sarà in grado di fare questo: return SomeMethod(out string temp). Vedi di più qui: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Oskar

7
In C # 7.0 è possibile utilizzare una variabile di sola scrittura temporanea come:return SomeMethod(out _);
Reiner

64

No, ma un'altra grande alternativa è che il metodo usi una classe modello generica per i parametri opzionali come segue:

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

Quindi puoi usarlo come segue:

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

19
È una soluzione molto ragionevole, ma una cosa da tenere presente è che il compilatore non imporrà il requisito che il parametro out deve essere assegnato prima di uscire dal metodo.
Ken Smith,

2
Mi piace, ma se non si desidera creare una nuova classe, è possibile simularla passando in un singolo array di elementi.
zumalifeguard,

30

In realtà c'è un modo per farlo che è consentito da C #. Questo torna a C ++ e viola piuttosto la bella struttura orientata agli oggetti di C #.

UTILIZZARE QUESTO METODO CON ATTENZIONE!

Ecco il modo in cui dichiari e scrivi la tua funzione con un parametro opzionale:

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

Quindi chiama la funzione in questo modo:

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

Per ottenere questo da compilare, dovrai abilitare il codice non sicuro nelle opzioni del progetto. Questa è una soluzione davvero confusa che di solito non dovrebbe essere usata, ma se per qualche decisione strana, arcana, misteriosa, ispirata alla gestione, REALMENTE hai bisogno di un parametro di uscita opzionale in C #, allora questo ti permetterà di farlo.


6

ICYMI: Incluso nelle nuove funzionalità per C # 7.0 elencate qui , "discards" è ora consentito come parametri out sotto forma di _, per farti ignorare i parametri che non ti interessano:

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

PS se sei anche confuso con la parte "out var x", leggi anche la nuova funzione su "Out Variables" sul link.


è _ o * "p.GetCoordinates (out int x, out *); // Mi interessa solo x"
Onur Topal

sembra che stavo cercando una versione precedente del documento.
Onur Topal,

2

No, ma puoi usare un delegato (ad es. Action) Come alternativa.

Ispirato in parte dalla risposta di Robin R di fronte a una situazione in cui pensavo di volere un parametro di uscita opzionale, ho invece utilizzato un Actiondelegato. Ho preso in prestito il suo codice di esempio da modificare per l'uso Action<int>al fine di mostrare le differenze e le somiglianze:

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

Questo ha il vantaggio che la variabile opzionale appare nel sorgente come un normale int (il compilatore lo avvolge in una classe di chiusura, piuttosto che noi lo avvolge esplicitamente in una classe definita dall'utente).

La variabile necessita di un'inizializzazione esplicita perché il compilatore non può presumere che Actionverrà chiamato prima della chiusura della chiamata di funzione.

Non è adatto a tutti i casi d'uso, ma ha funzionato bene per il mio caso d'uso reale (una funzione che fornisce dati per un test unitario e in cui un nuovo test unitario necessitava di accesso a uno stato interno non presente nel valore restituito).


0

Utilizzare un metodo sovraccarico senza il parametro out per chiamare quello con il parametro out per C # 6.0 e versioni precedenti. Non sono sicuro del motivo per cui un C # 7.0 per .NET Core è anche la risposta corretta per questo thread quando è stato specificamente chiesto se C # 4.0 può avere un parametro di uscita opzionale. La risposta è no!


-2

Che ne dici di questo?

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

Devi ancora passare un valore al parametro da C # ma è un parametro di riferimento opzionale.


8
"Devi ancora passare un valore al parametro da C #" ... Questo non lo rende facoltativo.
Arturo Torres Sánchez,

C # ignora l' [Optional]annotazione. Questo non aiuta.
ToolmakerSteve

-4
void foo(ref int? n)
{
    return null;
}

1
Per favore, aggiungi qualche spiegazione sul tuo codice per far capire facilmente a tutti il ​​concetto
techspider,

1
Sebbene questo codice possa rispondere alla domanda, fornendo un contesto aggiuntivo per quanto riguarda il perché e / o il modo in cui risponde alla domanda migliorerebbe significativamente il suo valore a lungo termine. Si prega di modificare la risposta di aggiungere qualche spiegazione.
Toby Speight,

2
Ciò comporterà un errore di sintassi poiché il metodo ha un tipo di ritorno nullo. Inoltre, non risponde alla domanda.
Nathan Montez,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.