Câu trả lời của Yuval và David về cơ bản là chính xác; Tổng hợp:
- Việc sử dụng một biến cục bộ chưa được gán là một lỗi có khả năng và trình biên dịch này có thể được phát hiện với chi phí thấp.
- Việc sử dụng một trường hoặc phần tử mảng chưa được gán ít có khả năng là một lỗi và khó phát hiện điều kiện hơn trong trình biên dịch. Do đó, trình biên dịch không cố gắng phát hiện việc sử dụng biến chưa được khởi tạo cho các trường và thay vào đó dựa vào việc khởi tạo thành giá trị mặc định để làm cho hành vi của chương trình xác định.
Một người bình luận cho câu trả lời của David hỏi tại sao không thể phát hiện việc sử dụng một trường không được gán thông qua phân tích tĩnh; đây là điểm tôi muốn mở rộng trong câu trả lời này.
Trước hết, đối với bất kỳ biến, cục bộ hoặc mặt khác, trong thực tế không thể xác định chính xác liệu một biến được gán hay không được gán. Xem xét:
bool x;
if (M()) x = true;
Console.WriteLine(x);
Câu hỏi "được x giao?" tương đương với "M () có trả về đúng không?" Bây giờ, giả sử M () trả về giá trị đúng nếu Định lý cuối cùng của Fermat đúng với tất cả các số nguyên nhỏ hơn một trăm triệu và ngược lại là sai. Để xác định xem x có được gán chắc chắn hay không, về cơ bản, trình biên dịch phải tạo ra một bằng chứng về Định lý cuối cùng của Fermat. Trình biên dịch không thông minh.
Vì vậy, những gì trình biên dịch làm thay cho người địa phương là thực hiện một thuật toán nhanh và đánh giá quá cao khi một địa phương không được gán chắc chắn. Đó là, nó có một số điểm tích cực sai, trong đó có ghi "Tôi không thể chứng minh rằng địa phương này được chỉ định" mặc dù bạn và tôi biết điều đó. Ví dụ:
bool x;
if (N() * 0 == 0) x = true;
Console.WriteLine(x);
Giả sử N () trả về một số nguyên. Bạn và tôi biết rằng N () * 0 sẽ là 0, nhưng trình biên dịch không biết điều đó. (Lưu ý: trình biên dịch C # 2.0 đã biết điều đó, nhưng tôi đã loại bỏ tối ưu hóa đó, vì thông số kỹ thuật không nói rằng trình biên dịch biết điều đó.)
Được rồi, vậy chúng ta biết gì cho đến nay? Thật không thực tế khi người dân địa phương có được câu trả lời chính xác, nhưng chúng ta có thể đánh giá quá cao việc không được chỉ định với giá rẻ và nhận được một kết quả khá tốt mà lỗi ở phía "làm cho bạn sửa chương trình không rõ ràng của bạn". Điều đó thật tốt. Tại sao không làm điều tương tự cho các lĩnh vực? Đó là, làm một trình kiểm tra chuyển nhượng xác định mà đánh giá quá cao với giá rẻ?
Chà, có bao nhiêu cách để một địa phương được khởi tạo? Nó có thể được chỉ định trong văn bản của phương thức. Nó có thể được gán trong lambda trong văn bản của phương thức; rằng lambda có thể không bao giờ được gọi, vì vậy những bài tập đó không liên quan. Hoặc nó có thể được chuyển thành "out" cho phương thức anothe, tại thời điểm đó chúng ta có thể giả sử nó được gán khi phương thức trở lại bình thường. Đó là những điểm rất rõ ràng tại đó địa phương được chỉ định và chúng ở ngay trong cùng một phương thức mà địa phương được khai báo . Xác định phân công xác định cho người dân địa phương chỉ yêu cầu phân tích địa phương . Các phương thức có xu hướng ngắn - ít hơn một triệu dòng mã trong một phương thức - và vì vậy việc phân tích toàn bộ phương thức khá nhanh chóng.
Bây giờ những gì về các lĩnh vực? Các trường có thể được khởi tạo trong một hàm tạo. Hoặc một trình khởi tạo trường. Hoặc hàm tạo có thể gọi một phương thức cá thể khởi tạo các trường. Hoặc hàm tạo có thể gọi một phương thức ảo khởi tạo các trường. Hoặc hàm tạo có thể gọi một phương thức trong một lớp khác , có thể nằm trong thư viện , khởi tạo các trường. Các trường tĩnh có thể được khởi tạo trong các hàm tạo tĩnh. Các trường tĩnh có thể được khởi tạo bởi các hàm tạo tĩnh khác .
Về cơ bản, trình khởi tạo cho một trường có thể ở bất kỳ đâu trong toàn bộ chương trình , bao gồm cả các phương thức ảo sẽ được khai báo trong các thư viện chưa được viết :
// Library written by BarCorp
public abstract class Bar
{
// Derived class is responsible for initializing x.
protected int x;
protected abstract void InitializeX();
public void M()
{
InitializeX();
Console.WriteLine(x);
}
}
Có phải là một lỗi để biên dịch thư viện này? Nếu có, BarCorp phải sửa lỗi như thế nào? Bằng cách gán một giá trị mặc định cho x? Nhưng đó là những gì trình biên dịch đã làm.
Giả sử thư viện này là hợp pháp. Nếu FooCorp viết
public class Foo : Bar
{
protected override void InitializeX() { }
}
là rằng một lỗi? Làm thế nào là trình biên dịch phải tìm ra điều đó? Cách duy nhất là thực hiện phân tích toàn bộ chương trình theo dõi tĩnh khởi tạo của mọi trường trên mọi đường dẫn có thể đi qua chương trình , bao gồm các đường dẫn liên quan đến việc lựa chọn phương thức ảo khi chạy . Vấn đề này có thể khó tùy ý ; nó có thể liên quan đến việc thực hiện mô phỏng hàng triệu đường dẫn điều khiển. Phân tích các luồng điều khiển cục bộ mất vài giây và phụ thuộc vào kích thước của phương pháp. Phân tích các luồng điều khiển toàn cầu có thể mất nhiều giờ vì nó phụ thuộc vào độ phức tạp của mọi phương thức trong chương trình và tất cả các thư viện .
Vậy tại sao không làm một phân tích rẻ hơn mà không phải phân tích toàn bộ chương trình, và chỉ đánh giá quá cao thậm chí còn nghiêm trọng hơn? Chà, đề xuất một thuật toán hoạt động mà không quá khó để viết một chương trình chính xác thực sự biên dịch và nhóm thiết kế có thể xem xét nó. Tôi không biết bất kỳ thuật toán như vậy.
Bây giờ, người bình luận đề xuất "yêu cầu một nhà xây dựng khởi tạo tất cả các trường". Đó không phải là một ý tưởng tồi. Trên thực tế, một ý tưởng không tồi là C # đã có tính năng đó cho các cấu trúc . Một constructor struct được yêu cầu để gán chắc chắn tất cả các trường vào thời điểm ctor trở lại bình thường; hàm tạo mặc định khởi tạo tất cả các trường thành các giá trị mặc định của chúng.
Còn lớp học thì sao? Chà, làm thế nào để bạn biết rằng một constructor đã khởi tạo một trường ? Các ctor có thể gọi một phương thức ảo để khởi tạo các trường và bây giờ chúng ta quay lại vị trí tương tự như trước đây. Structs không có các lớp dẫn xuất; các lớp học có thể. Là một thư viện chứa một lớp trừu tượng cần thiết để chứa một hàm tạo khởi tạo tất cả các trường của nó? Làm thế nào để lớp trừu tượng biết những giá trị nào các trường nên được khởi tạo?
John đề nghị đơn giản là cấm các phương thức gọi trong một ctor trước khi các trường được khởi tạo. Vì vậy, tóm tắt, các tùy chọn của chúng tôi là:
- Làm cho các thành ngữ lập trình phổ biến, an toàn, thường xuyên được sử dụng bất hợp pháp.
- Thực hiện một phân tích toàn bộ chương trình đắt tiền khiến cho quá trình biên dịch mất nhiều giờ để tìm kiếm các lỗi có thể không có ở đó.
- Dựa vào khởi tạo tự động đến các giá trị mặc định.
Nhóm thiết kế đã chọn phương án thứ ba.