2012-04-12 33 views
20

Tại sao ném ngoại lệ trong kết quả của hàm tạo trong tham chiếu null? Ví dụ, nếu chúng ta chạy các mã bên dưới giá trị của giáo viên là null, trong khi st.teacher thì không (đối tượng Teacher được tạo ra). Tại sao?Tại sao ném ngoại lệ trong kết quả của hàm tạo trong tham chiếu null?

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

} 

Trả lời

37

Các nhà xây dựng không bao giờ hoàn tất, do đó việc chuyển nhượng không bao giờ xảy ra. Nó không phải là null được trả lại từ constructor (hoặc có một "null object" - không có khái niệm như vậy). Nó chỉ là bạn không bao giờ gán một giá trị mới cho teacher, do đó, nó giữ lại giá trị trước đó của nó.

Ví dụ, nếu bạn sử dụng:

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

... sau đó bạn vẫn sẽ có "Đây là hợp lệ" giáo viên. Biến name vẫn sẽ không được gán một giá trị ở chỗ Teacher đối tượng mặc dù, như constructor Teacher bạn đang thiếu một dòng như:

this.name = name; 
+0

cảm ơn vì lời giải thích tuyệt vời, tôi đã chỉnh sửa "đối tượng rỗng" trong câu hỏi thành "tham chiếu null". –

+0

Giải thích tuyệt vời, bạn cũng chỉ chứng minh rằng không phải đối tượng khởi tạo trong C# luôn giữ «null». Tôi bắt đầu nghi ngờ trong đó vì khi tôi cố gắng sử dụng trong Visual Studio một đối tượng có thể được uninitialized trong một điều kiện nhất định, nhưng dù sao nó đã được kiểm tra kiểm tra cho «null», và tiếp theo được sử dụng, trình biên dịch hiển thị một lỗi về uninitialized biến. Sau khi tôi khởi tạo một cách rõ ràng đối tượng bằng «null», lỗi đã biến mất. Nhờ bạn, bây giờ tôi biết rằng nó chỉ là một lỗi của Visual Studio. –

+4

@ Hi-Angel: Không, nó không phải là một lỗi. Đó là sự khác biệt giữa một trường * và một biến cục bộ. Một trường có giá trị mặc định và có thể được sử dụng mà không bao giờ được đặt - biến cục bộ * không thể * được đọc cho đến khi được gán chắc chắn. –

3

Khi bạn ném ngoại lệ vào một hàm tạo, bạn sẽ phá vỡ cấu trúc của đối tượng. Vì vậy, nó không bao giờ kết thúc và do đó, không có đối tượng để trở về. Trong thực tế, toán tử gán đó (teacher = new Teacher("", st);) không bao giờ được thực thi vì ngoại lệ phá vỡ ngăn xếp gọi.

Và hàm tạo của giáo viên vẫn viết một tham chiếu đến chính nó (đối tượng đang được xây dựng) vào thuộc tính của đối tượng Sinh viên. Nhưng bạn không bao giờ nên thử sử dụng đối tượng giáo viên này sau đó, vì nó chưa được xây dựng. Nó có thể dẫn đến hành vi không xác định.

12

Nguyên nhân bạn đang kiểm tra số tham chiếu .

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

nhưng

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

Bạn đang ném ngoại lệ sau khi chuyển nhượng 'student.teacher = này ; // Dòng này được thực hiện nếu (name.Length < 5) // Điều này được kiểm tra và đúng trong trường hợp được chỉ định ném ArgumentException mới ("Tên phải dài ít nhất 5 ký tự"); // BAM: Ngoại lệ ném vào đây. '

Vì vậy, giá trị của giáo viên là null (như trường hợp ngoại lệ được ném trước khi hoàn thành hàm tạo), trong khi st.teacher thì không!

-1

Công việc chính của hàm tạo là khởi tạo đối tượng. Nếu có một ngoại lệ trong khởi tạo chính nó thì không có điểm trong việc có một đối tượng không được khởi tạo đúng cách. Do đó, ném ngoại lệ từ một hàm tạo ra trong đối tượng null.

+0

Điều này không chính xác; nó không có kết quả gì cả, như được chỉ ra trong các câu trả lời khác. – Ashe

0

Nếu Foo là một loại tài liệu tham khảo, báo cáo kết quả Foo = new FooType(); sẽ xây dựng một đối tượng và sau đó, sau khi các nhà xây dựng đã hoàn thành, lưu trữ một tham chiếu vào Foo. Nếu hàm tạo đưa ra ngoại lệ, mã sẽ lưu trữ tham chiếu vào Foo sẽ bị bỏ qua mà không cần viết Foo.

Trong trường hợp:

  • Một tuyên bố như trên xảy ra trong vòng một try/catch khối
  • Bản tuyên bố có thể đạt được mà không Foo đã được viết trước đó.
  • Foo biến cục bộ được xác định trong ngữ cảnh xung quanh khối catch.
  • Có thể thực hiện bắt đầu tại điểm bắt để đạt được tuyên bố đọc Foo mà không cần phải viết sau catch.

Trình biên dịch sẽ giả định rằng nỗ lực sau để đọc Foo có thể được thực hiện mà không cần Foo đã được viết và sẽ từ chối biên dịch trong trường hợp đó. Trình biên dịch sẽ cho phép Foo để được đọc mà không cần phải được viết tay, tuy nhiên, nếu:

  • Foo là một lĩnh vực lớp học, hoặc một lĩnh vực của một struct được lưu trữ trong một lĩnh vực lớp học, một lĩnh vực của một struct được lưu trữ trong một lĩnh vực của cấu trúc được lưu trữ trong trường lớp, v.v.
  • Foo được chuyển thành tham số out cho phương thức (được viết bằng ngôn ngữ khác C#) không lưu bất kỳ thứ gì, và tuyên bố đọc foo sẽ chỉ có thể truy cập được nếu phương pháp đã trở lại bình thường thay vì thông qua ngoại lệ.

Trong trường hợp cũ, Foo sẽ có giá trị được xác định là null. Trong trường hợp thứ hai, giá trị của Foo có khả năng sẽ là lần đầu tiên nó được tạo trong khi thực thi phương thức; nếu được tạo lại trong vòng lặp, nó có thể chứa null hoặc giá trị cuối cùng được ghi vào nó sau lần cuối cùng được tạo; tiêu chuẩn không cụ thể về những gì sẽ xảy ra trong tình huống đó.

Lưu ý rằng nếu FooType có bất cứ điều gì giống như một nhà xây dựng bình thường, sẽ không bao giờ Foo = new FooType();nguyên nhânFoo để trở thành null nếu nó không phải trước đó. Nếu câu lệnh hoàn thành bình thường, Foo sẽ giữ tham chiếu đến một thể hiện của loại chính xác FooType mà trước đó không có tham chiếu nào tồn tại ở bất kỳ đâu trong vũ trụ; nếu nó ném một ngoại lệ, nó sẽ không ảnh hưởng đến Foo theo bất kỳ cách nào.

Các vấn đề liên quan