2011-08-18 49 views
5

các hàm tạo được thực hiện theo thứ tự từ trên xuống dưới I.E. đầu tiên của cơ sở tiếp theo là cơ sở đầu tiên. Sự sắp xếp này dựa trên một đảm bảo OOP quan trọng rằng một đối tượng (cơ sở ở đây) phải luôn được khởi tạo trước khi nó có thể được sử dụng (ở đây trong hàm tạo của lớp dẫn xuất).Tại sao bộ khởi tạo trường của một lớp dẫn xuất được thực hiện trước khi các trình khởi tạo của lớp cơ sở

Tôi tự hỏi tại sao các trình khởi tạo trường không tuân theo nguyên tắc này trong C#? Am i thiếu cái gì ở đây?

Tôi đã xem xét tính hữu dụng của nguyên tắc này với các trình khởi tạo trường. Tôi có một lớp cơ sở với một tài sản trả về đối tượng Identity. Mỗi lớp dẫn xuất có trường kho lưu trữ riêng của nó mà tôi đã khởi tạo bằng cách sử dụng trình khởi tạo trường (sử dụng hàm tạo mặc định). Gần đây tôi đã quyết định rằng lớp kho lưu trữ cũng phải được cung cấp với đối tượng Identity vì vậy tôi đã giới thiệu một đối số bổ sung trong hàm tạo kho lưu trữ. Nhưng tôi bị mắc kẹt để tìm hiểu:

public class ForumController : AppControllerBase 
{ 
     ForumRepository repository = new ForumRepository(Identity); 
    // Above won't compile since Identity is in the base class. 

    // ... Action methods. 
} 

Bây giờ tôi chỉ còn một lựa chọn đó là đầy đặn mỗi bộ điều khiển của tôi với một constructor mặc định chỉ cho để thực hiện công việc khởi tạo của đối tượng kho đậm đà bản sắc.

+0

Không, lý do mà nó không biên dịch không phải là ở tất cả các 'Identity' là trong lớp cơ sở, nó chỉ đơn giản bởi vì nó là một thành viên cá thể. Vì vậy, câu hỏi trong tiêu đề không liên quan đến những gì bạn đang cố gắng làm ... – Guffa

Trả lời

6

Tại sao bộ khởi tạo trường của lớp dẫn xuất được thực thi trước trình khởi tạo trường lớp cơ sở?

Câu hỏi hay. Tôi đã trả lời câu hỏi của bạn trong các bài đăng trên blog từ năm 2008:

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

0

Trình khởi tạo trường được thực hiện trong hàm tạo và khi hàm tạo trong lớp cơ sở được gọi đầu tiên, tất cả các trình khởi tạo trường cũng được thực hiện trước khi hàm tạo dẫn xuất thực thi.

Ví dụ:

public class Base { 

    // field initialiser: 
    private string _firstName = "Arthur"; 

    public string FirstName { get { return _firstName;}} 
    public string LastName { get; private set; } 

    // initialiser in base constructor:  
    public Base() { 
    LastName = "Dent"; 
    } 

} 

public class Derived : Base { 

    public string FirstNameCopy { get; private set; } 
    public string LastNameCopy { get; private set; } 

    public Derived() { 
    // get values from base class: 
    FirstNameCopy = FirstName; 
    LastNameCopy = LastName; 
    } 

} 

Test:

Derived x = new Derived(); 
Console.WriteLine(x.FirstNameCopy); 
Console.WriteLine(x.LastNameCopy); 

Output:

Arthur 
Dent 
+0

Tôi nghĩ rằng bạn đã không nhận được quan điểm của tôi. Tôi đã biết những gì bạn đang nói.Câu hỏi của tôi là các trình khởi tạo trường đã cho trong cả hai lớp cơ sở và có nguồn gốc, tại sao một lớp dẫn xuất được thực hiện trước và sau đó là lớp cơ sở? Nếu bạn thấy các nhà xây dựng, nó là đối diện chính xác. –

+0

@Varun K: Tôi hiểu, bạn đang cố truy cập một trường từ trình khởi tạo trường. Bạn không thể làm điều đó, bởi vì bạn không thể sử dụng các thành viên cá thể từ một trình khởi tạo, và điều đó không liên quan gì đến các trường đang ở trong các lớp khác nhau. – Guffa

+0

Tôi biết tôi không thể làm điều đó :-). nhưng tôi muốn biết lý do đằng sau nó. –

0

Initializers Dòng được không có nghĩa là một sự thay thế cho nhà xây dựng.

Theo tài liệu MSDN, tất cả các trình khởi tạo trường được thực hiện trước các hàm tạo. Tuy nhiên, một hạn chế trên Field Initializer là chúng không thể tham chiếu đến các instance fields khác. (http://msdn.microsoft.com/en-us/library/ms173118(v=vs.80).aspx)

Hạn chế là do thực tế không có cách nào để xác định thứ tự đúng và phụ thuộc để thực thi Trình khởi tạo trường ở cấp trình biên dịch.

Bạn sẽ phải viết một hàm tạo mặc định để đạt được những gì bạn muốn. Tuy nhiên, bạn có thể thử với một trường tĩnh; nó là một thực hành tồi và thậm chí có thể không phù hợp với thiết kế của bạn.


Chỉnh sửa để làm rõ câu hỏi hỏi trong comment:

Theo như một lớp học có nguồn gốc là có liên quan, các lĩnh vực lớp cơ sở trông như nhau cho dù chúng được khởi tạo thông qua việc khởi tạo hoặc các nhà xây dựng. Thông tin này không thể được cung cấp cho các lớp con vì điều này có nghĩa là để lộ việc triển khai lớp cơ sở (mà là hoàn toàn riêng tư đối với lớp cơ sở).

Điều này ngụ ý rằng lớp dẫn xuất Initializer không có cách nào để tìm hiểu xem tất cả các trường lớp cơ sở đã được khởi tạo trước khi truy cập chúng chưa. Do đó hành vi không được phép.

+0

Tôi đã biết điểm giới hạn này (đúng thứ tự và sự phụ thuộc) nhưng nếu bạn thấy, điều này hoàn toàn áp dụng cho các trường trong cùng một lớp. Tôi đang đặt câu hỏi theo quan điểm của các khởi tạo trường trong cơ sở so với trường khởi tạo trong lớp dẫn xuất –

2

Trong C#, một constructor chạy trình tự:

 
    Perform all field initializers 
    Chain to base class constructor 
    Execute user-supplied code for constructor 

Trong vb.net, trình tự là:

 
    Chain to base class constructor 
    Perform all field initializers 
    Execute user-supplied code for constructor 

Không có khung-bas lý do tại sao C# thực hiện mọi thứ theo thứ tự mà nó thực hiện, bằng chứng là vb.net có thể thực hiện và thực hiện chúng theo một trình tự khác. Sự biện minh thiết kế cho phương pháp C# là không có khả năng một đối tượng nào được tiếp xúc với thế giới bên ngoài trước khi tất cả các khởi tạo trường (cho các trường dẫn xuất cũng như các lớp cơ sở) đã chạy; vì một hàm tạo lớp cơ sở có thể phơi bày một đối tượng với thế giới bên ngoài, thực thi yêu cầu đó có nghĩa là các trình khởi tạo trường phải chạy trước hàm tạo lớp cơ sở.

Cá nhân, tôi không thấy rằng lý giải đặc biệt thuyết phục. Có rất nhiều kịch bản mà trong đó nó sẽ không thể đặt trường thành các giá trị hữu ích mà không có thông tin sẽ không có sẵn trước khi hàm tạo cơ sở đã chạy. Bất kỳ mã nào có hàm tạo cơ sở có thể trưng ra các cá thể được xây dựng một phần cần được chuẩn bị cho khả năng đó. Mặc dù có những lúc hữu ích khi chỉ định rằng trình khởi chạy trường sẽ chạy "sớm", tôi nghĩ có nhiều tình huống hơn khi chúng hữu ích để chúng có thể truy cập đối tượng non trẻ (trong một số biện pháp vì tôi tin rằng các trường lớp có các giá trị cần được coi là các bất biến trong suốt vòng đời của một cá thể lớp nên khi thiết thực được thiết lập một cách khai báo thông qua các khởi tạo hơn là bắt buộc trong một hàm tạo).

Ngẫu nhiên, một tính năng tôi muốn xem trong cả vb.net và C# sẽ là phương tiện khai báo trường được khởi tạo tham số và trường giả. Nếu một lớp có trường tham số được khởi tạo của một tên và kiểu nhất định, mọi hàm tạo cho lớp đó không có chuỗi khác trong cùng một lớp phải chứa các tham số có tên và kiểu thích hợp. Các giá trị của các trường đó sẽ được đặt trong hàm tạo trước khi bất kỳ điều gì khác được thực hiện và sẽ có thể truy cập được vào các trình khởi tạo trường khác. Các trường giả sẽ hoạt động giống như các trường, ngoại trừ việc chúng sẽ chỉ có thể sử dụng trong các trình khởi tạo trường và sẽ được triển khai dưới dạng các biến cục bộ trong các hàm tạo. Một tính năng như vậy sẽ làm cho nhiều loại cấu trúc thuận tiện hơn. Ví dụ, nếu một loại là nghĩa vụ phải giữ một ví dụ mảng đặc biệt trong suốt cuộc đời của mình, có thể nói:

 
    readonly param int Length; 
    readonly ThingType[] myArray = new ThingType[Length]; 

dường như đẹp hơn vì phải hoặc xây dựng các mảng trong constructor lớp (trong đó không thể xảy ra cho đến sau khi hàm tạo cơ sở đã chạy) hoặc (cho vb.net) phải truyền độ dài cho hàm tạo lớp cơ sở, sau đó có thể sử dụng nó để thiết lập trường (sau đó chiếm không gian trong lớp ngay cả khi giá trị của nó-- như Length ở trên - có thể không cần thiết).

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