21

コンストラクターで例外をスローすると、参照がnullになるのはなぜですか?たとえば、以下のコードを実行すると、teacherの値はnullになりますが、st.teacherはnullになりません(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;
  }

}
4

6 に答える 6

40

コンストラクターが完了することはないため、割り当ては発生しません。コンストラクターからnullが返されるわけではありません(または「nullオブジェクト」があります-そのような概念はありません)。に新しい値を割り当てることは決してないteacherので、以前の値を保持します。

たとえば、次を使用する場合:

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

...それでも「これは有効です」という先生がいます。ただし、コンストラクターに次のような行がないため、変数にはそのオブジェクトnameの値は割り当てられません。TeacherTeacher

this.name = name;
于 2012-04-12T08:25:28.337 に答える
13

参照をチェックしているからです。

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

しかし

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." );
}
于 2012-04-12T08:23:00.400 に答える
3

コンストラクターで例外をスローすると、オブジェクトの構造が壊れます。したがって、それは決して終了せず、したがって、返すオブジェクトはありません。teacher = new Teacher( "", st );実際、例外によって呼び出しスタックが中断されるため、その代入演算子( )は実行されません。

また、Teacherコンストラクターは、それ自体(構築中のオブジェクト)への参照をStudentオブジェクトのプロパティに書き込みます。ただし、このTeacherオブジェクトは作成されていないため、後で使用してはいけません。未定義の動作が発生する可能性があります。

于 2012-04-12T08:21:40.347 に答える
1

が参照型の場合Foo、ステートメントFoo = new FooType();はオブジェクトを作成し、コンストラクターが完了した後、参照をに格納しますFoo。コンストラクターが例外をスローした場合、参照を格納するコードは書き込まれFooずにスキップFooされます。

次の場合:

  • 上記のようなステートメントはtry/catchブロック内で発生します
  • Foo事前に書いてなくても声明にたどり着くことができます。
  • Foocatchブロックを囲むコンテキストで定義されたローカル変数。
  • Fooキャッチから開始する実行は、の後に書き込まずに読み取るステートメントに到達する可能性がありますcatch

コンパイラーは、後者の読み取りの試行が書き込まれていFooなくても実行できると想定しFoo、その場合はコンパイルを拒否します。ただし、コンパイラはFoo、次の場合に、書き込まれていなくても読み取りを許可します。

  • Fooクラスフィールド、またはクラスフィールドに格納されている構造体のフィールド、クラスフィールドに格納されている構造体のフィールドに格納されている構造体のフィールドなどです。
  • Foooutは、何も格納しないメソッド(C#以外の言語で記述された)にパラメーターとして渡されます。読み取りfooを行うステートメントは、メソッドが例外ではなく正常に返された場合にのみ到達可能です。

前者の場合、Fooはの定義された値を持ちますnull。後者の場合、の値はFoo、メソッドの実行中に最初に作成されたときにnullになる可能性があります。ループ内で再作成された場合null、最後に作成された後の最後の値が含まれている可能性があります。この基準は、その状況で何が起こるかについて具体的ではありません。

FooType通常のコンストラクターに似たものがある場合、それが以前でなければnullになるFoo = new FooType();ことは決してないこと 注意してください。Fooステートメントが正常に完了すると、以前はユニバースのどこにも参照が存在していなかったFoo正確なタイプのインスタンスへの参照が保持されます。FooType例外がスローされても、影響はありませんFoo

于 2015-06-03T16:48:19.347 に答える
0

割り当ての後に例外をスローしています'student.teacher= this; //この行は次の場合に実行されますif(name.Length <5)//これはチェックされ、指定された場合にtrueになりますthrow new ArgumentException( "名前は少なくとも5文字の長さである必要があります。"); // BAM:ここで例外がスローされます。」

したがって、teacherの値はnullです(コンストラクターの完了前にスローされる例外として)が、st.teacherはnullではありません!

于 2012-04-12T08:36:50.763 に答える
-1

コンストラクターの主な仕事は、オブジェクトを初期化することです。初期化自体に例外がある場合、適切に初期化されていないオブジェクトを持っていても意味がありません。したがって、コンストラクターから例外をスローすると、nullオブジェクトになります。

于 2012-04-12T08:44:55.440 に答える