Подтвердить что ты не робот

Почему бросание исключения в конструкторе приводит к нулевой ссылке?

Почему бросание исключения в конструкторе приводит к нулевой ссылке? Например, если мы запускаем коды ниже значения учителя, это значение равно null, а st.teacher - нет (создается объект Teacher). Почему?

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

}
4b9b3361

Ответ 1

Конструктор никогда не завершается, поэтому присваивание никогда не происходит. Это не то, что null возвращается из конструктора (или что там "нулевой объект" - нет такого понятия). Просто вы никогда не назначаете новое значение teacher, поэтому оно сохраняет свое предыдущее значение.

Например, если вы используете:

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

... тогда у вас все еще будет учитель "Это действительно". Переменной переменной name по-прежнему не будет присвоено значение в этом объекте teacher, хотя в вашем конструкторе teacher отсутствует строка, например:

this.name = name;

Ответ 2

Потому что вы проверяете референции.

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

но

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

Ответ 3

Когда вы создаете исключение в конструкторе, вы разрушаете конструкцию объекта. Поэтому он никогда не заканчивался и, следовательно, нет объекта для возвращения. Фактически, этот оператор присваивания (teacher = new Teacher( "", st );) никогда не выполняется, поскольку исключение разбивает вызывающий стек.

И конструктор Учителя все еще записывает ссылку на себя (объект, создаваемый) в свойство объекта "Студент". Но вы никогда не должны пытаться использовать этот объект Учителя впоследствии, так как он не был построен. Это может привести к поведению undefined.

Ответ 4

Вы бросаете исключение после назначения 'student.teacher = это;//Эта строка выполнена     if (name.Length < 5)//Это отмечено и является истинным в указанном случае       throw new ArgumentException ( "Имя должно быть не менее 5 символов".);//BAM: Исключение выбрано здесь. '

Таким образом, значение учителя равно null (как исключение, возникшее перед завершением конструктора), а st.teacher - нет!

Ответ 5

Если Foo является ссылочным типом, оператор Foo = new FooType(); будет строить объект, а затем, после завершения конструктора, сохраните ссылку в Foo. Если конструктор генерирует исключение, код, который сохранит ссылку в Foo, будет пропущен без Foo, который был записан.

В тех случаях, когда:

  • Оператор, подобный приведенному выше, встречается в блоке try/catch
  • Заявление может быть достигнуто без Foo, написанного заранее.
  • Foo локальная переменная, определенная в контексте, окружающем блок catch.
  • Возможно, чтобы выполнение, начинающееся с catch, получило оператор, который читает Foo, не записав его после catch.

Компилятор предположит, что последняя попытка прочитать Foo может быть выполнена без Foo, которая была написана, и в этом случае откажется от компиляции. Компилятор позволит читать Foo без написания, если:

  • Foo - это поле класса или поле структуры, хранящееся в поле класса, поле структуры, хранящееся в поле структуры, хранящейся в поле класса, и т.д.
  • Foo передается в качестве параметра out методу (написанному на языке, отличном от С#), который ничего не хранит в нем, а оператор, который читает Foo, будет доступен только в том случае, если метод возвратился обычно, а не через исключение.

В первом случае Foo будет иметь определенное значение null. В последнем случае значение Foo, скорее всего, будет null при первом его создании во время выполнения метода; если он воссоздан в цикле, он может содержать null или последнее значение, которое будет записано в него после последнего его создания; стандарт не является конкретным о том, что произойдет в этой ситуации.

Обратите внимание, что если FooType имеет что-то похожее на обычный конструктор, Foo = new FooType(); никогда не приведет к тому, что Foo станет нулевым, если оно не было раньше. Если инструкция завершается нормально, Foo будет содержать ссылку на экземпляр точного типа FooType, к которому ранее ссылка не существовала где-либо в юниверсе; если он генерирует исключение, это никак не повлияет на Foo.

Ответ 6

Основная задача конструктора - инициализировать объект. Если в инициализации есть исключение, тогда нет смысла иметь объект, который не был правильно инициализирован. Следовательно, исключение исключения из конструктора приводит к нулевому объекту.