2012-04-12 11 views
20

Perché l'eccezione di lancio nel costruttore restituisce un riferimento null? Ad esempio, se eseguiamo i codici sotto il valore di teacher è null, mentre st.teacher non lo è (viene creato un oggetto Teacher). Perché?Perché l'eccezione di lancio nel costruttore restituisce un riferimento null?

using System; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
    static void Main(string[] args) 
    { 
     Test(); 
    } 

    private static void Test() 
    { 
     Teacher teacher = null; 
     Student st = new Student(); 
     try 
     { 
     teacher = new Teacher("", st); 
     } 
     catch (Exception e) 
     { 
     Console.WriteLine(e.Message); 
     } 
     Console.WriteLine((teacher == null)); // output True 
     Console.WriteLine((st.teacher == null)); // output False 
    } 
    } 

    class Teacher 
    { 
    public string name; 
    public Teacher(string name, Student student) 
    { 
     student.teacher = this; 
     if (name.Length < 5) 
     throw new ArgumentException("Name must be at least 5 characters long."); 
    } 
    } 

    class Student 
    { 
    public Teacher teacher; 
    } 

} 

risposta

37

Il costruttore non viene completata, quindi, l'assegnazione non si verifica mai. Non è che viene restituito nulla dal costruttore (o che c'è un "oggetto nullo" - non esiste un tale concetto). È solo che non si assegna mai un nuovo valore a teacher, quindi mantiene il valore precedente.

Per esempio, se si utilizza:

Teacher teacher = new Teacher("This is valid", new Student()); 
Student st = new Student(); 
try 
{ 
    teacher = new Teacher("", st); 
} 
catch (... etc ...) 

... allora avrai ancora l'insegnante "Questo è valido". La variabile name ancora non verrà assegnato un valore in quella Teacher oggetto, però, come il vostro Teacher costruttore manca una linea come ad esempio:

this.name = name; 
+0

grazie per la grande spiegazione, ho modificato "oggetto nullo" nella domanda in "riferimento null". –

+0

Ottima spiegazione, anche tu hai appena dimostrato che l'oggetto non inizializzato in C# mantiene sempre «null». Ho iniziato a dubitarne, perché quando ho provato a utilizzare in Visual Studio un oggetto che non poteva essere inizializzato in determinate condizioni, ma in ogni caso è stato controllato il controllo di "null", e successivamente utilizzato, il compilatore ha mostrato un errore sulla variabile non inizializzata. Dopo aver inizializzato in modo esplicito l'oggetto con «null», l'errore è scomparso. Grazie a te, ora so che è solo un bug di Visual Studio. –

+4

@ Hi-Angel: No, non è un bug. È la differenza tra un * campo * e una variabile locale. Un campo ha un valore predefinito e può essere utilizzato senza che sia mai stato impostato - una variabile locale * non può * essere letta fino a quando non viene assegnata definitivamente. –

3

Quando si genera un'eccezione in un costruttore, si interrompe la costruzione dell'oggetto. Quindi non ha mai finito e, quindi, non c'è alcun oggetto da restituire. Infatti, quell'operatore di assegnazione (teacher = new Teacher("", st);) non viene mai eseguito poiché l'eccezione interrompe lo stack chiamante.

E il costruttore Teacher scrive ancora un riferimento a se stesso (l'oggetto in costruzione) nella proprietà dell'oggetto Studente. Ma non dovresti mai provare a utilizzare questo oggetto Insegnante in seguito, poiché non è stato costruito. Potrebbe comportare un comportamento indefinito.

12

Causa che stai verificando i riferimenti .

try 
    { 
    teacher = new Teacher("", st); //this line raises an exception 
            // so teacher REMAINS NULL. 
            // it's NOT ASSIGNED to NULL, 
            // but just NOT initialized. That is. 
    } 
    catch (Exception e) 
    { 
    Console.WriteLine(e.Message); 
    } 

ma

public Teacher(string name, Student student) 
{ 
    student.teacher = this; //st.Teacher is assigned BEFORE exception raised. 
    if (name.Length < 5) 
    throw new ArgumentException("Name must be at least 5 characters long."); 
} 
0

si sta gettando l'eccezione dopo l'assegnazione 'student.teacher = questo ; // Questa riga viene eseguita if (name.Length < 5) // Viene verificato ed è true nel caso specificato throw new ArgumentException ("Nome deve essere lungo almeno 5 caratteri."); // BAM: Exception buttato qui.

Quindi il valore dell'insegnante è nullo (come eccezione generata prima del completamento del costruttore), mentre st.teacher non lo è!

-1

Il lavoro principale del costruttore è inizializzare l'oggetto. Se c'è un'eccezione nella stessa inizializzazione, allora non ha senso avere un oggetto che non è inizializzato correttamente. Quindi lanciare un'eccezione da un costruttore produce un oggetto nullo.

+0

Questo non è corretto; non risulta in nulla, come sottolineato in altre risposte. – Ashe

0

Se Foo è un tipo di riferimento, l'istruzione Foo = new FooType(); costruirà un oggetto e poi, dopo il costruttore ha completato, memorizzare un riferimento in Foo. Se il costruttore genera un'eccezione, il codice che memorizzerebbe il riferimento in Foo verrà saltato senza che sia stato scritto Foo.

Nei casi in cui:

  • Una dichiarazione come il precedente si è verificato all'interno di un try/catch blocco
  • La dichiarazione può essere raggiunto senza Foo essendo stato scritto in precedenza.
  • Foo una variabile locale definita in un contesto che circonda il blocco catch.
  • È possibile che l'esecuzione a partire dal fermo raggiunga un'istruzione che legge Foo senza che sia stata scritta dopo lo catch.

Il compilatore supporre che quest'ultimo tentativo di leggere Foo potrebbe essere eseguita senza Foo essendo stato scritto, e si rifiuterà di compilazione in questo caso. Il compilatore permette Foo da leggere senza essere stato scritto, tuttavia, se:

  • Foo è un campo di classe, o in un campo di una struttura memorizzata in un campo di classe, un campo di una struct memorizzato in un campo di una struttura memorizzata in un campo classe, ecc.
  • Foo viene passato come un parametro out a un metodo (scritto in una lingua diversa da C#) che non memorizza nulla e l'istruzione che legge foo solo essere raggiungibile se il metodo è stato restituito normalmente piuttosto che tramite eccezione.

Nel primo caso, Foo avrà un valore definito di null. In quest'ultimo caso, il valore di Foo sarà probabilmente nullo la prima volta che viene creato durante l'esecuzione di un metodo; se ricreato all'interno di un ciclo, può contenere null o l'ultimo valore da scrivere dopo l'ultima volta che è stato creato; lo standard non è specifico su cosa accadrà in quella situazione.

Nota che se FooType ha qualcosa di simile a un costruttore normale, Foo = new FooType(); sarà mai causaFoo a diventare nullo se non fosse prima. Se la dichiarazione viene completata normalmente, Foo manterrà un riferimento a un'istanza di tipo esatto FooType a cui in precedenza nessun riferimento era mai esistito nell'universo; se genera un'eccezione, non avrà alcun effetto su Foo in alcun modo.

Problemi correlati