2009-06-24 4 views
7

sto guardando un po 'di codice (Delphi 7) con i seguenti controllo è nella parte superiore di ogni chiamata di metodo per un oggetto specifico:Perché dovresti controllare se Assegnato (auto) nei metodi oggetto?

if not Assigned(self) then 
    raise Exception.CreateRes(@sAbstractError); 

    { Real code for this method} 

Credo che questo mi avrebbe impedito di tentare di chiamare un metodo su un puntatore a oggetti null. Ma otterrei un'eccezione non appena ho provato ad accedere ai dati dei membri in quel caso comunque, giusto?

È questo un tipo di standard che non ho mai visto prima? L'oggetto in questione deriva da TPersistent.

risposta

8

È possibile chiamare un metodo di istanza su un puntatore nullo anche se non è il tipo di cosa che si desidera eseguire deliberatamente. Quando ciò accade, l'esecuzione continua abbastanza felicemente finché non ha bisogno di accedere ai dati di istanza e poi tutto va bene.

In tal caso, il controllo di nil ti avviserà all'inizio della procedura in modo da poter eseguire un'operazione diversa, ad esempio la registrazione di una traccia di stack. Oppure potresti mettere un punto di interruzione sulla linea di rilancio in modo da poterti tuffare e vedere cosa sta succedendo.

Cioè, è qualcosa che potrei fare se avessi un bug specifico che stavo cercando di rintracciare dove veniva utilizzato un riferimento nullo.

Facendolo regolarmente mi colpisce come un odore di codice.

4

Vi sono scenari di violazione di accesso che possono portare a codice in esecuzione in memoria dove Self è nullo. Alcuni programmatori, invece di risolvere il vero problema, cercano di bypassare usando tali asserzioni, per prevenire corruzioni di sorta. Controllerei molto bene se l'eccezione si risolve mai in runtime, se è così - hai un codice bacato sotto le tue mani.

È possibile leggere il seguente articolo sull'argomento: When Self in Nil... You Know You are in Trouble.

Un'altra possibilità è legata agli eventi, è possibile leggere di più qui, con un sacco di esempi per tali test e spiegazioni corrispondenti: Multicast Events - Part 2.

+2

Non direi che questo codice di esempio ignora il vero problema. Al contrario, evidenzia esplicitamente il problema. –

10

Un errore chiaro che si lamenta di un puntatore nullo è migliore di una violazione di accesso che non dice come è successo.

+1

+1 per rilevare un puntatore nullo anziché una * possibile * violazione di accesso. –

+0

Quindi stai dicendo di codificare tutti i tuoi oggetti in questo modo? –

+0

È molto difficile che un puntatore nullo non venga spostato. Non possiedi la memoria laggiù. –

1

E quindi c'è sempre la possibilità che il codice non si arresti in modo anomalo durante l'esecuzione con nil Self. Esempio: se non accede a nessun campo dell'oggetto proprietario. In tal caso, questo test catturerebbe il problema che altrimenti non sarebbe rilevato.

Tuttavia, questo sta portando la programmazione difensiva all'estremo. Non ho mai (OK, quasi mai - solo quando sto dando la caccia ai wabbits ... errr ... schiacciando i bug) fallo.

+0

Anche se l'ho fatto - è una forma scadente usare un metodo non di classe come se fosse un metodo di classe. –

1

Sembra che l'intenzione originale per quel metodo sia che diventi un metodo astratto.

3

Questa è un'occasione che dovrebbe sicuramente portare a un arresto anomalo (ad esempio un'eccezione del sistema operativo come "letto dall'indirizzo 0x00000000"). Lanciare un'eccezione di linguaggio è semplicemente ridondante (e l'uso improprio di EAbstractError qui non lo rende migliore).

Il controllo di parametri di input validi non è un'attività possibile in un linguaggio non sicuro, in quanto non si otterrà mai la certezza, e quindi la gestione di parametri non validi non sarà mai coerente. (Perché si genera un'eccezione quando viene passato un puntatore nullo, ma non per 0x00000001, che è altrettanto non valido?). Per una discussione più tecnica su questa domanda, leggi il blog di Larry Osterman su why not to check for valid pointers.

Si noti un'eccezione: in Delphi, il metodo Free può essere chiamato su un puntatore nullo, che ovviamente richiede quel tipo di controllo.