Domanda

Diciamo che ho una funzione che deve restituire un valore intero. ma può anche fallire e devo sapere quando lo fa.

Qual è il modo migliore?

public int? DoSomethingWonderful()

o

public bool DoSomethingWonderful(out int parameter)

questa è probabilmente più una domanda di stile, ma sono ancora curioso di sapere quale opzione le persone prenderebbero.

Modifica: chiarimento, questo codice parla con una scatola nera (chiamiamola nuvola. no, una scatola nera. no, aspetta. nuvola. sì). Non mi interessa perché non è riuscito. Avrei solo bisogno di sapere se ho un valore valido o meno.

È stato utile?

Soluzione

Mi piace di più la versione nullable, perché è possibile utilizzare l'operatore null coalesce ?? su di esso, ad es .:

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;

Altri suggerimenti

Dipende da come pensi che dovrebbe apparire il codice chiamante. E quindi a cosa serve la tua funzione.

Generalmente, dovresti evitare gli argomenti. D'altra parte, potrebbe essere bello avere un codice come questo:

int parameter;
if (DoSomething(out paramameter))
{
  // use parameter
}

Quando hai un int nullable, sarebbe simile a questo:

int? result = DoSomething();
if (result != null)
{
  // use result
}

Questo è un po 'meglio perché non hai un argomento out, ma il codice che decide se la funzione è riuscita non sembra molto ovvio.

Non dimenticare che esiste un'altra opzione: usa le eccezioni. Fallo solo se il caso in cui la tua funzione fallisce è davvero un caso eccezionale e di tipo

try
{
  // normal case
  int result = DoSomething()
}
catch (SomethingFailedException ex)
{
  // exceptional case
}

Un vantaggio dell'eccezione è che non puoi semplicemente ignorarlo. Anche il caso normale è semplice da attuare. Se il caso eccezionale fosse qualcosa che potresti ignorare, non dovresti usare eccezioni.

Modifica: Hai dimenticato di menzionare: un altro vantaggio di un'eccezione è che puoi anche fornire informazioni perché l'operazione non è riuscita. Queste informazioni sono fornite dal tipo di eccezione, dalle proprietà dell'eccezione e dal testo del messaggio.

Perché non generare un'eccezione?

Seguirei il modello usato in qualche punto della libreria .Net come:

bool int.TryParse(string s, out value)
bool Dictionary.TryGetValue(T1 key, out T2 value)

Quindi direi:

public bool TryDoSomethingWonderful(out int parameter)

Dipende davvero da cosa stai facendo.

Null è una risposta significativa? In caso contrario, preferirei una chiamata al metodo bool TryDoSomethingWonderful (out int) . Ciò corrisponde al Framework.

Se, tuttavia, null è un valore restituito significativo, restituendo int? ha senso.

A meno che le prestazioni non siano la preoccupazione principale, è necessario restituire un int e generare un'eccezione in caso di errore.

Userei il secondo, perché probabilmente dovrei sapere subito se la chiamata è andata a buon fine, e in tal caso preferirei scrivere

int x;
if( DoSomethingWonderful( out x ) )
{
    SomethingElse(x);
}

di

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}

Sono favorevole all'utilizzo di un parametro di output. A mio avviso, questo è il tipo di situazione per cui è più adatto l'uso di parametri di output.

Sì, è possibile utilizzare l'operatore di coalescenza per mantenere il codice come una linea singola se e solo se si dispone di un valore alternativo che è possibile utilizzare nel resto del codice. Trovo spesso che non sia il caso per me, e preferirei eseguire un percorso di codice diverso da quello che farei se fossi riuscito a recuperare un valore.

        int value;
        if(DoSomethingWonderful(out value))
        {
            // continue on your merry way
        }
        else
        {
            // oops
            Log("Unable to do something wonderful");

            if (DoSomethingTerrible(out value))
            {
                // continue on your not-so-merry way
            }
            else
            {
                GiveUp();
            }
        }

Inoltre, se il valore che voglio recuperare è effettivamente nulla, l'utilizzo di una funzione con un parametro di output e un valore di ritorno booleano è, a mio avviso, il modo più semplice per dire la differenza tra " Non ho avuto successo in recupero del valore " e " Il valore che ho recuperato è nullo " ;. A volte mi interessa questa distinzione, come nel seguente esempio:

    private int? _Value;
    private bool _ValueCanBeUsed = false;

    public int? Value
    {
        get { return this._Value; }
        set
        {
            this._Value = value;
            this._ValueCanBeUsed = true;
        }
    }

    public bool DoSomethingTerrible(out int? value)
    {
        if (this._ValueCanBeUsed)
        {
            value = this._Value;
            // prevent others from using this value until it has been set again
            this._ValueCanBeUsed = false;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

Secondo me, l'unica ragione per cui la maggior parte delle persone tende a non usare i parametri di output è perché trovano la sintassi ingombrante. Tuttavia, ritengo davvero che l'utilizzo dei parametri di output sia la soluzione più appropriata a questo problema e ho scoperto che una volta che mi sono abituato ho trovato la sintassi molto preferibile per restituire un valore null.

Se c'è un solo modo in cui può fallire, o se non avrai mai bisogno di sapere perché è fallito, direi che probabilmente è più semplice e più facile andare con il valore di ritorno nullable.

Al contrario, se ci sono diversi modi in cui potrebbe non riuscire e il codice chiamante potrebbe voler sapere esattamente perché non è riuscito, quindi andare con il parametro out e restituire un codice di errore anziché un valore bool (in alternativa, è possibile generare un'eccezione , ma in base alla tua domanda, sembra che tu abbia già deciso di non gettare un'eccezione).

Dovresti piuttosto usare un tentativo di cattura. Sembra che il chiamante non sappia cosa potrebbe accadere?

Dovremmo controllare sia bool che out, oppure dovrei controllare sia restituisce null sia l'effettiva restituzione.

Lascia che il metodo faccia quello che dovrebbe, e nel caso in cui fallisse, fai sapere al chiamante che ha fallito e il chiamante ha richiesto come richiesto.

È interessante notare che la mia opinione personale oscilla in modo significativo in base alla natura del metodo. In particolare, se lo scopo del metodo è recuperare un singolo valore , invece di "fare qualcosa".

Esempio:

bool GetSerialNumber(out string serialNumber)

vs

string GetSerialNumber() // returns null on failure

Il secondo sembra più "naturale" per me in qualche modo, e allo stesso modo:

bool GetDeviceId(out int id)

vs

int? GetDeviceId() // returns null on failure`

Ma ammetto che questo rientra davvero nello "stile di codifica" territorio.

Oh, e anche io tenderei a favorire il lancio di eccezioni:

int GetDeviceId() // throws an exception on read failure

Non sono ancora venduto sul motivo per cui sarebbero così sbagliati. Possiamo avere una discussione su questo, Oren? ; -)

Non mi piace Microsoft " Prova " modello in cui il "fuori" Il parametro viene utilizzato per restituire un elemento dati. Tra l'altro, i metodi codificati in questo modo non possono essere utilizzati nelle interfacce covarianti. Preferirei vedere un metodo codificato come: T GetValue (out bool Successful) o forse T GetValue (out GetValueErrorEnum risultato); o T GetValue (out GetValueErrorInfo risultato ); se potrebbe essere necessario qualcosa oltre un vero / falso. Poiché ogni tipo di dati ha un valore predefinito legale, non c'è alcun problema nel decidere cosa restituire se la funzione fallisce. Il codice chiamante può facilmente dire:

  bool success;
  var myValue = Thing.GetValue(ref success);
  if (success)
    .. do something using myValue
  else
    .. ignore myValue

Sarebbe bello se .net e C # offrissero i veri parametri di "copia fuori" covarianti (il chiamante allocarebbe lo spazio per il risultato, passerebbe un puntatore a quello spazio alla funzione chiamata e quindi copierebbe lo spazio allocato al passaggio -in variabile solo dopo il ritorno della funzione).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top