Evitare eccezioni per l'ottimizzazione delle prestazioni

1

Nel nostro codice base, vedo un sacco di codice come questo

var error = ValidatePhoneNumber(userId, phoneNumber);

//if validation fails, return error
if(!string.IsNullOrEmpty(error))
{
    return error;
}

Se stavo scrivendo questo, avrei appena avuto ValidatePhoneNumber (userId, phoneNumber) lanciare l'errore e chiamare il metodo all'interno di un blocco try / catch. L'unica ragione per cui posso pensare a questo tipo di pratica è che gli errori di lancio / cattura possono diventare costosi. Le preoccupazioni relative alle prestazioni richiedono davvero questo tipo di gestione degli errori? Oppure un pattern try / catch sarebbe più utile in questa situazione?

Ho visto post come questo , e vedo che ci sono alcune piccole differenze di prestazioni nell'inclusione delle eccezioni, ma sono abbastanza grandi da interessare la maggior parte delle applicazioni?

(Lavoro principalmente nel framework .NET, se questo fa la differenza per la risposta)

    
posta Microsoft Excel 04.05.2017 - 00:05
fonte

5 risposte

9

Le prestazioni non sono un motivo per fare nulla di tutto ciò.

Se stai restituendo degli errori, piuttosto che buttarli, spero che tu lo faccia almeno in modo coerente.

Il motivo per cui non ha nulla a che fare con le prestazioni, la lingua o la correttezza del programma. Ha a che fare con umani confusi.

Se offri alcune funzioni / metodi a me che posso usare per risolvere i problemi, non confondermi avendo alcuni di essi restituire errori e alcune eccezioni. Scegli uno stile e attaccalo!

Questo non significa che questo codice sia necessariamente sbagliato. Era meglio non trovarsi in un contesto in cui ci si aspetta che le funzioni vengano lanciate. È meglio tornare a un contesto in cui ci si aspetta che le funzioni restituiscano errori.

Non posso dire se questo codice è buono o cattivo visto che ho solo questa funzione da seguire. Ma sicuramente mi alza le sopracciglia. Quello che devo vedere è ciò che gli altri metodi / funzioni stanno facendo. Se questo sta facendo cose diverse dagli altri intorno, sarà una brutta sorpresa.

Se pensi che il motivo per cui non dovremmo usare eccezioni per il controllo di flusso è perché l'acquisizione di una traccia di stack è troppo costosa, non hai mai sentito parlare di Knuth .

Il motivo per cui non dovresti usare le eccezioni per il controllo del flusso è perché sono quasi difficili da seguire come goto . Quindi usali quando le cose vanno male. Usali per spostare la logica di gestione degli errori lontano dalla logica del "giorno di sole". Non usarli perché pensi che siano un modo divertente per diramare il tuo codice. Non mi stupire con i tuoi modi intelligenti di usarli. Ho abbastanza cose a cui pensare.

    
risposta data 04.05.2017 - 00:54
fonte
3

Come guida di programmazione C #: Cose da evitare When Throwing Exceptions

Exceptions should not be used to change the flow of a program as part of ordinary execution. Exceptions should only be used to report and handle error conditions.

La convalida fa parte della logica di esecuzione "ordinaria" nell'applicazione.
Se sposti il metodo di convalida in try .. catch block, quale tipo di eccezione intendi catturare, Exception ? In tal caso, cosa succede se il metodo di validazione non è correlato alla validazione?
L'utilizzo di eccezioni come logica di convalida può essere fuorviante per altri sviluppatori o lettori del codice (la maggior parte delle volte i programmatori leggono il codice).

Sono abbastanza sicuro che la convalida può essere fatta in un modo più leggibile usando le eccezioni.

var phoneNumber = ValidatePhoneNumber(string number);
var outputText = phoneNumber.ToText();

Dove

public interface IPhoneNumber
{
    string ToText();
}

public class ValidPhoneNumber
{
    public ValidPhoneNumber(string number) { _number = number } 
    public string ToText()
    {
        return $"Phone: {number}";
    }
}

public class InvalidPhoneNumber
{
    public ValidPhoneNumber(string errorMessage) { _message = errorMessage} 
    public string ToText()
    {
        return _message;
    }
}

E convalida il metodo

public IPhoneNumber ValidatePhoneNumber(string rawNumber)
{
    // do your validation logic get error message if possible

    if (IsValid)
    {
        return new ValidPhoneNumber(validNumber);
    }
    else
    {
        return new InvalidPhoneNumber(errorMessage);
    }
}

Con l'approccio sopra, introducendo InvalidPhoneNumber si nascondono i dettagli di "implementazione" sulla convalida e si utilizza semplicemente l'implementazione "non valida" dell'interfaccia.

    
risposta data 04.05.2017 - 00:45
fonte
0

Le eccezioni, come dice il nome, dovrebbero essere usate per circostanze eccezionali .

Convalidare l'input dell'utente e trovarlo in errore non è assolutamente eccezionale. Almeno non con gli utenti che conosco. Fanno sempre errori. Quindi gestirli è la cosa normale che il programma deve fare.

Ora se il tuo numero di telefono proviene da un database super sicuro e dovresti essere assolutamente valido, quindi controllarlo di nuovo e trovarlo invalido dovrebbe essere un'eccezione.

Quindi questa non è una decisione tecnica. Chiediti se la condizione di errore è normale o un'eccezione.

Prendiamo un altro esempio: il metodo IsMyPizzaDoneYet() restituisce un bool . Semplicemente perché c'è una reale possibilità che tu fossi impaziente e non è ancora finito. Niente da vedere, andare avanti. Potrebbe anche gettare un HolyShitTheOvenIsOnFireException se il forno è in fiamme. Perché questa è una eccezione .

Per non confondere le persone, se il punto di un metodo sta generando un'eccezione, basta chiamarlo così . Basandosi sull'esempio sopra, in cui all'improvviso si scopre che il database è incoerente e manca di un numero di telefono valido nonostante il fatto che li abbia controllati quando gli utenti vengono creati:

public void SendTextMessageToKnownUser(int userID, string message)
{
   var phone = GetPhoneNumberFromSecureDatabase(userID);

   ThrowIfNotValid(phone);

   phone.Send(message);
}
    
risposta data 04.05.2017 - 15:07
fonte
0

Per evidenziare l'aspetto prestazioni della domanda:

Il mio riferimento obbligatorio: Risolvi il problema: sei a conoscenza del fatto che hai generato oltre 40.000 eccezioni nelle ultime 3 ore?

Riepilogo esecutivo: in .NET, il lancio di un'eccezione è costoso. Non farlo su percorsi di hot code regolari.

Il take-away è sempre lo stesso, le eccezioni dovrebbero essere eccezionali, quindi se si convalida l'input, usare un'eccezione potrebbe essere un problema di prestazioni se il metodo per convalidare l'input non è usato nella GUI , ma all'improvviso viene utilizzato per convalidare, ad esempio, un file csv di input con record 1M. Meglio non usare le eccezioni per ogni record sbagliato lì.

Detto questo, la mia opinione sarebbe approssimativamente:

void ValidatePhoneNumberFromInteractiveContext(...) {
  ValidatePhoneNumber_Throws(userId, phoneNumber);
  // The interactive caller knows how to format and display
  // exceptions
}

/or/

ErrorObject ValidatePhoneNumberFromBatchContext(...) {
  return ValidatePhoneNumber_WithError(userId, phoneNumber);
}

Nota che il primo approccio funziona davvero correttamente solo se la tua eccezione contiene informazioni sufficienti per creare un" messaggio "decente sul sito di cattura.

    
risposta data 05.05.2017 - 10:33
fonte
0

Il prodotto su cui stai lavorando è un lavoro in formato derivato eseguito in C o progettato per funzionare con C.

Se è così, non è per motivi di prestazioni perché le eccezioni non vengono utilizzate.

Nell'esempio che tu dai, non ha importanza come vengono gestiti gli errori, solo che sono gestiti in modo coerente.

Se la persona che ti dice questo è il tuo comando tecnico o qualcuno che non ti guida, non lo inganni. Ti rendi conto che è per altri motivi che alla fine avrai bisogno del loro aiuto.

    
risposta data 05.05.2017 - 13:26
fonte

Leggi altre domande sui tag