Come configurare smartphone e PC. Portale informativo
  • casa
  • Windows 10
  • Winforms corretta gestione delle eccezioni in c. Debug e gestione delle eccezioni

Winforms corretta gestione delle eccezioni in c. Debug e gestione delle eccezioni

costanti di compilazione condizionale... Consente la definizione di costanti utilizzate successivamente nel metodo di stampa condizionale WriteIf delle classi Debug e Trace. La potenza di questo meccanismo è che le costanti possono essere modificate nel file di configurazione del progetto senza modificare il codice del progetto o richiederne la ricompilazione.

Debug e Visual Studio .Net Toolbox

L'ambiente strumentale dello studio fornisce al programmatore la più ampia gamma di possibilità per tracciare lo stato di avanzamento dei calcoli e tracciare gli stati in cui si trova il processo di calcolo. Poiché tutti i moderni ambienti di strumenti sono organizzati in modo simile e sono ben noti ai programmatori che lavorano, non mi dilungherò a descrivere le capacità dell'ambiente.

La gestione delle eccezioni

Non importa quanto sia affidabile il codice scritto, non importa quanto sia accurato il debug, ci saranno violazioni delle specifiche nella versione rilasciata per la produzione e la manutenzione. Le ragioni di ciò sono le leggi sopra menzionate tecnici del programma... L'ultimo errore rimane nel sistema, ci sono utenti che non conoscono le specifiche e se le specifiche possono essere violate, questo evento accadrà un giorno. Tale situazioni eccezionali la continuazione del programma diventa impossibile (un tentativo di eseguire un'operazione di divisione per zero non autorizzata, tentativi di scrivere in un'area di memoria protetta, un tentativo di aprire un file inesistente, un tentativo di ottenere un database inesistente record), o in una situazione che si verifica, l'applicazione dell'algoritmo porterà a risultati errati.

Cosa fare se situazione eccezionale? Naturalmente, esiste sempre un modo standard: segnalare l'errore che si è verificato e interrompere l'esecuzione del programma. È chiaro che questo è accettabile solo per applicazioni innocue; anche per i giochi per computer, questo metodo non è adatto, per non parlare delle applicazioni critiche!

Nei linguaggi di programmazione per l'elaborazione situazioni eccezionali sono stati proposti diversi approcci.

C / C ++ Gestione delle eccezioni

Lo stile di programmazione C è caratterizzato dalla descrizione dei metodi di classe come funzioni booleane che restituiscono true se il metodo termina normalmente e false se si verifica. situazione eccezionale... La chiamata al metodo era inline nell'istruzione If, ​​che gestisce un errore in caso di fallimento del completamento del metodo:

bool MyMethod (...) (...) if! MyMethod () (// gestione degli errori) (// esecuzione normale)

Gli svantaggi di questo schema sono evidenti. Primo, ci sono poche informazioni sulla causa dell'errore, quindi ulteriori informazioni devono essere passate attraverso i campi della classe o attraverso gli argomenti del metodo. In secondo luogo, il blocco di elaborazione è incorporato in ogni chiamata, il che porta a un rigonfiamento del codice.

Pertanto, in C / C ++, viene utilizzato lo schema di blocco try / catch, la cui essenza è la seguente. La sezione del programma in cui potrebbe esserci situazione eccezionale, viene eseguito sotto forma di un try-block custodito. Se durante la sua esecuzione si verifica situazione eccezionale, quindi l'esecuzione del try-block con la classificazione dell'eccezione viene interrotta. Uno dei blocchi catch che corrisponde al tipo di eccezione inizia a gestire questa eccezione. Esistono due di questi schemi utilizzati in C / C ++. Uno di loro - schema di curriculum- corrisponde alle cosiddette eccezioni strutturali, o C. Il secondo schema è senza rinnovo- corrisponde alle eccezioni C++. Nel primo schema, il gestore di eccezioni, il blocco catch, restituisce il controllo a un punto del blocco try. Nel secondo schema, il controllo non ritorna al blocco try.

Con alcune differenze sintattiche schema di curriculum utilizzato nei linguaggi VB / VBA.

Schema di gestione delle eccezioni in C#

Il linguaggio C# ha ereditato lo schema delle eccezioni C++, apportandovi le proprie modifiche. Consideriamo lo schema in modo più dettagliato e iniziamo con la sintassi della costruzione prova-cattura-finalmente:

prova (...) cattura (T1 e1) (...) ... cattura (Tk ek) (...) finalmente (...)

Ovunque un blocco sia sintatticamente consentito nel testo di un modulo, il blocco può essere protetto aggiungendo la parola chiave try. Il blocco try può essere seguito da blocchi catch chiamati blocchi del gestore delle eccezioni, potrebbero essercene molti, potrebbero essere assenti. Completa questa sequenza finalmente blocco- blocco di finalizzazione, che può anche essere assente. L'intera costruzione può essere annidata: il blocco di prova può includere la costruzione prova-cattura-finalmente.

Lanciare eccezioni. Creazione di oggetti eccezione

Il corpo di un blocco try può contenere situazione eccezionale portando a lanciare eccezioni... formalmente lanciare un'eccezione si verifica quando viene eseguita un'istruzione throw. Questa istruzione viene spesso eseguita nelle viscere del sistema operativo quando il sistema di comando o la funzione API non possono svolgere il proprio lavoro. Ma questa affermazione può far parte del testo del programma del blocco try ed essere eseguita quando, a seguito dell'analisi, diventa chiaro che un'ulteriore operazione normale è impossibile.

Sintatticamente, l'istruzione throw è:

lanciare [espressione]

La clausola throw specifica un oggetto di una classe che eredita dalla classe Exception. Di solito è una nuova espressione che crea un nuovo oggetto. Se è assente, l'eccezione corrente viene generata nuovamente. Se viene generata un'eccezione dal sistema operativo, classifica l'eccezione stessa, crea un oggetto della classe corrispondente e compila automaticamente i suoi campi.

Nel modello che stiamo considerando, le eccezioni sono oggetti, la cui classe è un discendente della classe Exception. Questa classe e i suoi numerosi discendenti fanno parte della FCL, sebbene siano sparsi in diversi namespace. Ciascuna classe definisce uno specifico tipo di eccezione secondo la classificazione adottata nel .Net Framework. Qui ci sono solo alcuni classi di eccezione dallo spazio dei nomi System: ArgumentException, ArgumentOutOfRangeException, ArithmeticException, BadImageFormatException, DivideByZeroException, OverflowException. Lo spazio dei nomi System.IO contiene classi di eccezione relativi a problemi di I/O: DirectoryNotFoundException, FileNotFoundException e molti altri. Nomi di tutti classi di eccezione terminare con la parola Eccezione. Hai il permesso di creare il tuo classi di eccezione ereditandoli dalla classe Exception.

Quando viene eseguita l'istruzione throw, viene creato un oggetto te, la cui classe TE caratterizza l'eccezione corrente, e i campi contengono informazioni sulla situazione eccezionale... L'esecuzione dell'istruzione throw fa terminare il normale processo di calcolo. Se ciò accade in un tentativo di blocco protetto, inizia lo stadio "catturare" un'eccezione uno dei gestori di eccezioni.

Catturare un'eccezione

Il blocco catch - il gestore eccezioni ha la seguente sintassi:

cattura (T e) (...)

La classe T specificata nell'intestazione catch-block deve appartenere a classi di eccezione... Un blocco catch con un argomento formale e di classe T è potenzialmente in grado di catturare l'attuale eccezione te di classe TE se e solo se te è l'assegnazione compatibile con e. In altre parole, potenziale cattura significa che l'assegnamento e = te è valido, il che è possibile quando TE è un discendente di T. Un gestore la cui classe T è la classe Eccezione è manipolatore universale, è potenzialmente in grado di catturare qualsiasi eccezione, poiché ne sono tutti discendenti.

Possono esserci molti potenziali invasori; solo uno cattura un'eccezione: quello che è il primo nella lista di controllo. Qual è l'ordine di controllo? È abbastanza naturale. Innanzitutto, i gestori vengono controllati nell'ordine in cui seguono il blocco try e il primo potenziale invasore diventa attivo, cattura l'eccezione e la gestisce. Ciò rende chiaro che l'ordine dei blocchi catch è estremamente importante. I primi sono i più gestori specializzati, tanto più che aumenta la versatilità. Quindi, prima dovrebbe esserci un gestore di eccezioni DivideByZeroException, seguito da Main. Se non è presente alcun potenziale catcher di eccezioni, verrà attivato il gestore standard, interrompendo l'esecuzione del programma e inviando un messaggio corrispondente.

Catturare le eccezioni

Considerando che .NET Framework include un gran numero di classi di eccezioni predefinite, sorge la domanda: come si utilizzano nel codice per rilevare condizioni errate? Per far fronte a possibili situazioni di errore nel codice C#, il programma viene solitamente suddiviso in blocchi di tre tipi differenti:

    blocchi Tentativo incapsulare codice che fa parte delle normali azioni di un programma che può potenzialmente incontrare gravi situazioni di errore.

    blocchi prendere incapsulare il codice che gestisce le situazioni di errore che si verificano nel codice di un blocco try. È anche un luogo conveniente per la registrazione degli errori.

    blocchi finalmente incapsulare il codice che ripulisce qualsiasi risorsa o esegue altre azioni che normalmente dovrebbero essere eseguite alla fine dei blocchi try or catch. È importante capire che questo blocco viene eseguito indipendentemente dal fatto che venga generata o meno un'eccezione.

Prova e cattura

La base della gestione delle eccezioni C# è la coppia di parole chiave try and catch. Queste parole chiave funzionano insieme e non possono essere utilizzate separatamente. Quella che segue è una forma generale di definizione dei blocchi try/catch per gestire le eccezioni:

try (// Blocco di codice per verificare la presenza di errori.) catch (ExcepType1 exOb) (// Gestore di eccezioni di tipo ExcepType1.) catch (ExcepType2 exOb) (// Gestore di eccezioni di tipo ExcepType2.) ...

dove ExceptType è il tipo dell'eccezione che viene generata. Quando un'eccezione viene generata da un'istruzione try, viene catturata dall'istruzione pairing catch, che quindi gestisce l'eccezione. A seconda del tipo di eccezione, viene eseguita anche l'istruzione catch corrispondente. Quindi, se i tipi dell'eccezione generata e quella specificata nell'istruzione catch sono gli stessi, questa istruzione viene eseguita e tutto il resto viene ignorato. Quando viene rilevata un'eccezione, la variabile exOb dell'eccezione ottiene il suo valore. Infatti, specificare la variabile exOb è facoltativo. Quindi, non è necessario specificarlo se il gestore di eccezioni non ha bisogno dell'accesso all'oggetto eccezione, cosa che accade abbastanza spesso. Il tipo dell'eccezione è sufficiente per gestire l'eccezione.

Tieni presente, tuttavia, che se non viene generata alcuna eccezione, il blocco try termina come al solito e tutte le istruzioni catch vengono ignorate. L'esecuzione del programma riprende alla prima istruzione successiva all'istruzione catch finale. Pertanto, l'istruzione catch viene eseguita solo se viene generata un'eccezione.

Diamo un'occhiata a un esempio in cui gestiremo un'eccezione generata quando un numero viene diviso per 0:

Utilizzo del sistema; utilizzando System.Collections.Generic; utilizzando System.Linq; utilizzando System.Text; namespace ConsoleApplication1 (class Program (static int MyDel (int x, int y) (return x / y;) static void Main () (try (Console.Write ("Enter x:"); int x = int.Parse (Console .ReadLine ()); Console.Write ("Enter y:"); int y = int.Parse (Console.ReadLine ()); int result = MyDel (x, y); Console.WriteLine ("Result:" + risultato);) // Gestisce l'eccezione che si verifica quando si divide per zero catch (DivideByZeroException) (Console.WriteLine ("Divisione per 0 rilevata !!! \ n"); Main ();) // Gestisce l'eccezione quando il numero viene immesso in modo errato nel catch della console (FormatException) (Console.WriteLine ("Questo NON è un numero !!! \ n"); Main ();) Console.ReadLine ();)))

Questo semplice esempio illustra chiaramente la gestione di un'eccezione quando si divide per 0 (DivideByZeroException), nonché un errore personalizzato quando si immette un non numero (FormatException).

Conseguenze del mancato rilevamento delle eccezioni

Catturare una delle eccezioni standard, come nell'esempio sopra, ha un altro vantaggio: impedisce il crash del programma. Non appena viene lanciata un'eccezione, deve essere catturata da qualche pezzo di codice in un certo punto del programma. In generale, se un'eccezione non viene rilevata nel programma, verrà rilevata dal sistema runtime. Ma il punto è che il sistema in esecuzione emetterà un messaggio di errore e interromperà l'esecuzione del programma.

Un'eccezione è un'istanza di una classe che è stata ereditata in modo esplicito e implicito dalla classe base System.Exception.

Le variabili dichiarate in un blocco try escono dall'ambito quando il controllo viene passato a un blocco catch o infine. Alla fine del blocco try, anche se non è successo nulla (nessun errore si è verificato), il controllo viene automaticamente trasferito al blocco finalmente, che dovrebbe contenere le istruzioni per liberare risorse.

Quando viene rilevato un errore, il codice genera un'eccezione (crea un'istanza della classe di eccezione) e la emette come segue:

Lancia una nuova IndexOutOfRangeException ("Hai inserito:" + userInput);

Non appena il compilatore incontra un'istruzione throw all'interno di un blocco try, cerca immediatamente il blocco catch corrispondente.

Le istruzioni throw non devono necessariamente essere nello stesso metodo del blocco try; il blocco try continua a funzionare anche se il controllo viene trasferito ad altri metodi.

L'istruzione throw può essere in qualsiasi metodo chiamato mentre il blocco try è in esecuzione - non deve essere nello stesso metodo in cui è definito il blocco try.

I gestori di eccezioni per la classe derivata (IndexOutOfRangeException) devono precedere la classe base (Exception) (!)

Se il gestore catch è scritto come: catch (...) allora significa che è responsabile di qualsiasi codice (per qualsiasi eccezione che si verifica), inclusi quelli scritti non in C# o non gestiti.

È un requisito C# che solo le istanze di una classe derivata da System.Exception possano essere lanciate come eccezioni.

While (true) // ciclo infinito (Console.Write ("Seleziona una voce di menu da 0 a 5 o per uscire:"); string userInput = Console.ReadLine (); if (userInput == "") (Console.WriteLine ("Hai premuto quindi esci ..."); break;) try (int x = int.Parse (userInput); switch (x) (caso 0: Console.WriteLine ("Elemento 0 selezionato"); break; case 1 : Console.WriteLine ("Elemento selezionato 1"); break; case 2: Console.WriteLine ("Elemento selezionato 2"); break; case 3: Console.WriteLine ("Elemento selezionato 3"); break; case 4: Console . WriteLine ("Elemento selezionato 4"); break; case 5: Console.WriteLine ("Elemento selezionato 5"); break; default: throw new IndexOutOfRangeException (); break;)) catch (IndexOutOfRangeException e) (Console.WriteLine ( " Valore non valido. Scegli una cifra da ");) catch (FormatException e) (Console.WriteLine (" Errore di conversione, potresti aver inserito una stringa ... ");) catch (Exception e) (Console.WriteLine (" Qualche altro errore ");) catch() // gestore per codice non gestito e codice in una lingua diversa accidenti)

Proprietà e metodi System.Exception

if (ErrorCondition == true) (Exception myException = new ClassMyException ("Help!"); myException.Source = "Application Name"; myException.HelpLink = "myHelpFile.txt"; throw myException;)

Il codice non ha bisogno di generare eccezioni dalla classe generale System.Exception - non dà un'idea della natura dello stato di errore (!). Ci sono due classi importanti nella gerarchia:

SystemException è destinato alle eccezioni generalmente generate dal runtime .NET o alle eccezioni di natura generale e possono essere generate da quasi tutte le applicazioni. La sottoclasse rappresenta errori fatali e non fatali.

ApplicationException: rappresenta una base destinata a qualsiasi classe di eccezione definita da terze parti.

IOException (spazio dei nomi System.IO) relativo alla lettura e alla scrittura di dati in un file.

Viene generata una StackOverflowException quando il blocco di memoria allocato per lo stack è pieno. Un overflow dello stack può verificarsi, ad esempio, quando un metodo inizia a chiamare se stesso in modo ricorsivo. Un tentativo di lettura al di fuori dei limiti di un file è una causa comune di un'EndOfStreamException. Viene generata un'eccezione OverflowException, ad esempio, quando si tenta di eseguire il cast di un int contenente -40 a un uint in un contesto verificato.

I dolori di prova annidati vengono utilizzati per due motivi:

- per modificare il tipo dell'eccezione generata (utilizzando la proprietà InnerException, che contiene un collegamento a qualsiasi eccezione generata);

- gestione delle diverse eccezioni in diverse parti del codice.

Definire le proprie classi di eccezione

Definire le proprie eccezioni è un compito abbastanza semplice, dal momento che raramente è necessario aggiungere loro i propri metodi. È solo necessario implementare il costruttore per garantire che il costruttore della classe base venga chiamato correttamente.

Classe LandLineSpyFoundException: ApplicationException (public LandLineSpyFoundException (string spyName): base ("spyon" + spyName) () public LandLineSpyFoundException (string spyName, Exception innerException): base (spyName, innerException) ())

Buona giornata! Oggi parleremo di come gestire gli errori nei programmi scritti in C#.

Voglio notare subito che gli errori nei programmi possono essere suddivisi condizionatamente in due gruppi: errori al momento della compilazione del programma (in fase di compilazione) ed errori durante l'esecuzione del programma. Questo tutorial parlerà di errori di runtime!

Ricordiamo un esempio della lezione precedente in cui l'utente doveva inserire due numeri interi da tastiera. Quindi ho ancora chiesto di inserire esattamente numeri ed esattamente numeri interi. Come mai? Fino a quando, perché se inserisci in quel programma non un numero, ma solo testo, o non un numero intero, ma un numero frazionario, allora ci sarà un errore di runtime! È per questo motivo che oggi vi parlo dei meccanismi e dei principi della gestione degli errori nei programmi.

Quindi, se sospettiamo che il nostro programma possa avere errori di runtime, ad esempio a causa del fatto che l'utente ha inserito dati errati, dobbiamo occuparci di gestire questi errori. L'approccio alla gestione di tali situazioni consiste nelle seguenti regole: viene allocato un blocco di codice, durante l'esecuzione del quale possono verificarsi errori; viene allocato un blocco di codice responsabile della gestione degli errori che si sono verificati.

Un blocco di codice in cui possono verificarsi errori è indicato da una parola chiave Tentativo seguito da parentesi graffe (che racchiudono operazioni potenzialmente pericolose). Tale blocco dovrebbe essere immediatamente seguito da un blocco di elaborazione degli errori, indicato dalla parola chiave prendere, e tra parentesi dopo questa parola, denota il tipo di errori che elabora il blocco dati di codice, dopo la parentesi chiusa, seguita dalle parentesi graffe, all'interno delle quali viene implementata l'elaborazione. Schematicamente, sembra così:

Try (// Blocco di codice potenzialmente pericoloso) catch (error_type) (// Gestione degli errori) // Ulteriori istruzioni del programma

E funziona così se nel blocco Tentativo, non si è verificato alcun errore, quindi blocca prendere non viene eseguito e il controllo viene trasferito, ad es. viene eseguito il blocco successivo prendere operatore. Ma se all'interno del blocco Tentativo si è verificato un errore, quindi l'esecuzione di questo blocco viene interrotta e il controllo viene trasferito al blocco prendere, e solo allora vengono eseguiti gli operatori che seguono questo blocco.

In pratica, dopo un blocco Tentativo, potrebbero esserci diversi blocchi prendere, per la gestione separata di diversi tipi di errori.

Modifichiamo il codice del programma della lezione precedente, aggiungiamo il controllo e la gestione degli errori che possono verificarsi quando l'utente inserisce dati errati.

// Blocco try potenzialmente pericoloso (// Richiede all'utente di inserire il primo numero Console.Write ("Inserisci il primo numero e premi Invio:"); // Ottieni la prima stringa di riga firstString = Console.ReadLine (); // Converti la prima riga in numero int firstArg = Convert.ToInt32 (firstString); // Richiedi all'utente di inserire il secondo numero Console.Write ("Inserisci il primo numero e premi Invio:"); // Ottieni la seconda stringa string secondString = Console.ReadLine (); // Conversione della seconda stringa in un numero int secondArg = Convert.ToInt32 (secondString); // Aggiunta di due variabili int result = firstArg + secondArg; // Output del risultato Console.WriteLine ("Il risultato di aggiungere i numeri immessi:" + risultato.ToString ()); ) // Blocco di gestione degli errori, SystemException è il tipo più comune di rilevamento degli errori (SystemException) (Console.WriteLine ("Si è verificato un errore durante l'esecuzione del programma, probabilmente sono stati inseriti dati errati!");)

Ora, se l'utente immette dati errati invece di un numero intero, verrà elaborato l'errore che si è verificato. Se inserisci questo codice nel " Principale ", compila ed esegui il programma, vedrai il seguente comportamento:

E così, in questa lezione, sono state presentate le informazioni di base relative al meccanismo di gestione degli errori (eccezioni) nel linguaggio C#. Nella prossima lezione parleremo della creazione e dell'utilizzo dei nostri metodi (funzioni) e torneremo sull'argomento della gestione degli errori più di una volta!

Principali articoli correlati