Why throwing exception in constructor results in a null reference?

20,091

Solution 1

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:

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

... then you'll still have the "This is valid" teacher. The name variable still won't be assigned a value in that Teacher object though, as your Teacher constructor is missing a line such as:

this.name = name;

Solution 2

Cause you're checking the referencies.

  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 );
  }

but

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." );
}

Solution 3

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.

Solution 4

If Foo is a reference type, the statement Foo = new FooType(); will construct an object and then, after the constructor has completed, store a reference into Foo. If the constructor throws an exception, the code which would store the reference into Foo will be skipped without Foo having been written.

In cases where:

  • A statement like the above occurs within a try/catch block
  • The statement can be reached without Foo having been written beforehand.
  • Foo a local variable defined in a context surrounding the catch block.
  • It is possible for execution starting at the catch to reach a statement which reads Foo without its having written in after the catch.

The compiler will assume that the latter attempt to read Foo could be executed without Foo having been written, and will refuse compilation in that case. The compiler will allow Foo 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 an out parameter to a method (written in a language other than C#) that doesn't store anything to it, and the statement which reads foo would only be reachable if the method had returned normally rather than via exception.

In the former case, Foo will have a defined value of null. In the latter case, the value of Foo will likely be null the first time it is created during the execution of a method; if re-created within a loop, it may contain null 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 cause Foo to become null if it wasn't before. If the statement completes normally, Foo will hold a reference to an instance of exact type FooType to which no reference had previously existed anywhere in the universe; if it throws an exception, it will not affect Foo in any way.

Share:
20,091
Setyo N
Author by

Setyo N

Updated on June 04, 2020

Comments

  • Setyo N
    Setyo N about 4 years

    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;
      }
    
    }
    
  • Setyo N
    Setyo N about 12 years
    thanks for the great explanation, I edited "null object" in the question to "null reference".
  • Asherah
    Asherah almost 11 years
    This is not correct; it doesn't result in anything at all, as pointed out in other answers.
  • Hi-Angel
    Hi-Angel over 9 years
    Great explanation, also you're just proved that not initialized object in C# always holds «null». I began doubt in that because when I tried to use in Visual Studio an object that could be uninitialized in a certain conditions, but anyway it was checked checked for «null», and next used, the compiler shown an error about uninitialized variable. After I explicitly initialized the object with «null», the error disappeared. Thanks to you, now I know that it is just a bug of Visual Studio.
  • Jon Skeet
    Jon Skeet over 9 years
    @Hi-Angel: Nope, it's not a bug. It's the difference between a field and a local variable. A field has a default value, and can be used without ever having been set - a local variable cannot be read until it's definitely assigned.