为什么扔在构造结果异常的空引用? 例如,如果我们运行下面老师的值的代码为空,而st.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;
}
}
构造函数永远不会完成,因此分配不会发生。 这并不是说空从构造函数返回(或者说有一个“空对象” - 有没有这样的概念)。 这只是你从来没有分配一个新的价值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;
因为你正在检查的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 );
}
但
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." );
}
当你在构造函数抛出一个异常,你打破对象的构造。 所以它永远不会结束,因此,没有对象返回。 事实上,赋值运算符( teacher = new Teacher( "", st );
)永远不会执行,因为异常打破了调用堆栈。
和教师的构造还是写入自身的引用(正在建设中的对象)到Student对象的属性。 但你永远不应该尝试使用这个老师事后对象,因为它并没有被构建。 这可能会导致不确定的行为。
如果Foo
是引用类型,则该语句Foo = new FooType();
将构建一个对象,然后, 在构造完成后 ,存储参考成Foo
。 如果构造函数抛出异常,这将存储参考到代码中Foo
将不被跳过Foo
已经被写入。
在以下情况:
- 像上述语句的内发生
try
/ catch
块 - 该声明可以在没有达到
Foo
已被预先写入。 -
Foo
在围绕一个上下文中定义的局部变量catch
块。 - 这是可能的执行开始在catch达到其内容的声明
Foo
而不之后它已经写在catch
。
编译器将假设后者试图读取Foo
可能不被执行Foo
已经被写入,并会在这种情况下拒绝编译。 编译器将允许Foo
而没有被写来读,但是,如果:
-
Foo
是一类字段,或者被存储在一个类域一个结构的一个字段中,存储在存储在一个类域一个结构的一个字段一个结构的一个字段中,等 -
Foo
作为一个传递out
参数的方法(用C#以外的语言),不存储任何东西给它,并把读到的声明foo
只会是可达的,如果该方法已经正常地返回,而不是通过例外。
在前一种情况下, Foo
将有一个定义的值null
。 在后一种情况下,值Foo
将可能是无效它的方法的执行期间产生的第一时间; 在循环中,如果重新创建的,它可能包含null
或最后一个值来创建它的最后一次后写入的; 标准不具体说明在这种情况下会发生什么。
请注意,如果FooType
有任何类似正常构造, Foo = new FooType();
会不会导致 Foo
成为空,如果它之前不是。 如果语句正常完成, Foo
将坚持确切类型的实例的引用FooType
到没有提及此前在宇宙中任何地方存在的; 如果它抛出一个异常,也不会影响Foo
以任何方式。
您分配“student.teacher后抛出异常=这一点; //如果(name.Length <5)//这是检查和是的情况下指定掷新的ArgumentException真时,执行该线(“名称必须是至少5个字符长。”); // BAM:异常这里抛出“。
因此,教师的价值是零(作为例外构造完成之前抛出),而st.teacher是不是!
构造函数的主要工作是初始化对象。 如果在初始化自身然后在具有不正确初始化的对象是没有意义的异常。 因此,从空对象的构造导致抛出异常。