Why throwing exception in constructor results in a null reference? For example, if we run the codes below the value of teacher is null, while st.teacher is not (a Teacher object is created). Why?
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;
}
}
Cause you're checking the referencies.
but
The constructor never completes, therefore the assignment never occurs. It's not that null is returned from the constructor (or that there's a "null object" - there's no such concept). It's just that you never assign a new value to
teacher
, so it retains its previous value.For example, if you use:
... then you'll still have the "This is valid" teacher. The
name
variable still won't be assigned a value in thatTeacher
object though, as yourTeacher
constructor is missing a line such as:When you throw an exception in a constructor, you break object's construction. So it's never finished and hence, there's no object to return. In fact, that assignment operator (
teacher = new Teacher( "", st );
) is never executed since exception breaks the calling stack.And the Teacher constructor still writes a reference to itself (the object being constructed) into the Student object's property. But you should never try using this Teacher object afterwards, since it has not been constructed. It may result in undefined behavior.
You are throwing the exception after the assignment 'student.teacher = this; //This line is executed if ( name.Length < 5 ) //This is checked and is true in the case specified throw new ArgumentException( "Name must be at least 5 characters long." );//BAM : Exception thrown here.'
So value of teacher is null (as exception thrown before completion of constructor), while st.teacher is not!
Main job of the constructor is to initialize the object. If there is an exception in initialization itself then no point in having an object which is not properly initialized. Hence throwing exception from a constructor results in null object.
If
Foo
is a reference type, the statementFoo = new FooType();
will construct an object and then, after the constructor has completed, store a reference intoFoo
. If the constructor throws an exception, the code which would store the reference intoFoo
will be skipped withoutFoo
having been written.In cases where:
try
/catch
blockFoo
having been written beforehand.Foo
a local variable defined in a context surrounding thecatch
block.Foo
without its having written in after thecatch
.The compiler will assume that the latter attempt to read
Foo
could be executed withoutFoo
having been written, and will refuse compilation in that case. The compiler will allowFoo
to be read without having been written, however, if:Foo
is a class field, or a field of a struct stored in a class field, a field of a struct stored in a field of a struct stored in a class field, etc.Foo
is passed as anout
parameter to a method (written in a language other than C#) that doesn't store anything to it, and the statement which readsfoo
would only be reachable if the method had returned normally rather than via exception.In the former case,
Foo
will have a defined value ofnull
. In the latter case, the value ofFoo
will likely be null the first time it is created during the execution of a method; if re-created within a loop, it may containnull
or the last value to written to it after the last time it was created; the standard is not specific about what will happen in that situation.Note that if
FooType
has anything resembling a normal constructor,Foo = new FooType();
will never causeFoo
to become null if it wasn't before. If the statement completes normally,Foo
will hold a reference to an instance of exact typeFooType
to which no reference had previously existed anywhere in the universe; if it throws an exception, it will not affectFoo
in any way.