Những người khác đã chỉ ra rằng đây không phải là trọng, nhưng vẫn còn nguyên câu hỏi ban đầu của bạn: tại sao bạn có thể làm điều đó? (Nhưng câu hỏi thực sự là "tại sao bạn có thể ẩn các phương pháp tĩnh".)
Đó là một tính năng không thể tránh khỏi hỗ trợ phiên bản độc lập của thành phần có chứa các lớp cơ sở và các thành phần sử dụng các lớp cơ sở đó.
Ví dụ, hãy tưởng tượng rằng thành phần CompB chứa lớp cơ sở và một số thành phần khác của CompD chứa một lớp dẫn xuất. Trong phiên bản 1 của CompB, có thể không có bất kỳ tài sản nào được gọi là LogName. Tác giả của CompD quyết định thêm một thuộc tính tĩnh gọi là LogName. Điều quan trọng cần hiểu tại thời điểm này là tác giả của v1 CompD không có ý định thay thế hoặc ẩn bất kỳ tính năng nào của lớp cơ sở - không có thành viên nào được gọi là LogName trong lớp cơ sở khi họ viết mã đó.
Bây giờ hãy tưởng tượng rằng một phiên bản mới của thư viện CompB được phát hành. Trong phiên bản mới này, tác giả đã thêm thuộc tính LogName. Điều gì sẽ xảy ra trong CompD? Các tùy chọn xuất hiện được:
- CompD không còn hoạt động vì LOGNAME nó giới thiệu cuộc đụng độ với LOGNAME thêm vào CompB
- Bằng cách nào đó làm cho LOGNAME của CompD thay thế các cơ sở CompB LOGNAME. (Nó không thực sự rõ ràng như thế nào điều này có thể làm việc với statics. Bạn có thể dự tính này với không statics mặc dù.)
- Điều trị hai thành viên LogName là thành viên hoàn toàn khác nhau có cùng tên. (Trong thực tế, họ không - chúng được gọi là BaseLogger.LogName và SpecificLogger.LogName. Nhưng vì trong C# chúng ta không cần phải hội đủ điều kiện tên thành viên với lớp, có vẻ như chúng giống nhau.)
NET chọn để làm 3. (và nó mà với cả hai tĩnh và không tĩnh học Nếu bạn muốn hành vi 2 -. thay thế - với phi tĩnh, sau đó căn cứ phải là virtual
và lớp bắt nguồn phải đánh dấu phương thức là override
để làm rõ rằng chúng cố ý ghi đè phương thức trong lớp cơ sở. C# sẽ không bao giờ làm phương thức của lớp dẫn xuất thay thế phương thức của lớp cơ sở trừ khi lớp dẫn xuất đã khai báo rõ ràng rằng đây là những gì chúng muốn.) Điều này có thể an toàn vì hai thành viên không liên quan ed - cơ sở LogName thậm chí không tồn tại ở điểm mà một trong những nguồn gốc đã được giới thiệu. Và điều này là thích hợp hơn để đơn giản phá vỡ vì phiên bản mới nhất của lớp cơ sở đã giới thiệu một thành viên mới.
Nếu không có tính năng này, sẽ không thể cho các phiên bản mới của Khuôn khổ .NET thêm thành viên mới vào các lớp cơ sở hiện tại mà không bị thay đổi đột ngột.
Bạn nói rằng hành vi không phải là những gì bạn mong đợi. Trên thực tế nó là chính xác những gì tôi mong đợi, và những gì bạn có thể muốn trong thực tế. BaseLogger không có ý tưởng rằng SpecificLogger đã giới thiệu thuộc tính LogName của riêng nó. (Không có cơ chế mà nó có thể bởi vì bạn không thể ghi đè lên các phương thức tĩnh.Và khi tác giả của SpecificLogger viết rằng tài sản LogName, hãy nhớ rằng họ đã viết chống lại v1 của BaseLogger mà không có một LogName, do đó, họ không có ý định rằng nó nên thay thế phương thức cơ bản hoặc. Vì cả lớp không muốn thay thế, việc thay thế rõ ràng sẽ là điều sai trái.
Kịch bản duy nhất mà bạn nên kết thúc trong tình huống này là vì hai lớp này nằm trong các thành phần khác nhau. (Rõ ràng là bạn có thể tạo ra một kịch bản khi chúng ở trong cùng một thành phần, nhưng tại sao bạn lại làm điều đó? Nếu bạn sở hữu cả hai đoạn mã và giải phóng chúng trong một thành phần duy nhất, nó sẽ trở nên điên rồ khi làm điều này.) Và vì vậy BaseLogger sẽ nhận được thuộc tính LogName của chính nó, đó là chính xác những gì sẽ xảy ra. Bạn có thể đã viết:
SpecificLogger.Log("test");
nhưng biên dịch C# thấy rằng SpecificLogger không cung cấp một phương pháp Log, vì vậy nó quay này vào:
BaseLogger.Log("test");
bởi vì đó là nơi mà các phương pháp Log được định nghĩa. Vì vậy, bất cứ khi nào bạn định nghĩa một phương thức trong một lớp dẫn xuất không cố gắng ghi đè lên một phương thức hiện có, trình biên dịch C# chỉ ra điều này trong siêu dữ liệu. Ví dụ: (Có một cài đặt "newslot" trong siêu dữ liệu của phương thức cho biết, phương pháp này có nghĩa là mới, không liên quan đến bất kỳ thứ gì trong lớp cơ sở.)
Nhưng điều này mang lại cho bạn sự cố nếu bạn muốn biên dịch lại CompD. Giả sử bạn có báo cáo lỗi do một số mã hoàn toàn không liên quan và bạn cần phải phát hành phiên bản CompD mới. Bạn biên dịch nó chống lại Verison mới của CompB. Nếu mã bạn đã viết không được phép, bạn sẽ không thực sự có khả năng - mã cũ đã được biên dịch sẽ hoạt động, nhưng bạn sẽ không thể biên dịch các phiên bản mới của mã đó, điều này sẽ hơi điên .
Và như vậy, để hỗ trợ kịch bản này (thẳng thắn hơi mơ hồ), chúng cho phép bạn thực hiện việc này. Họ tạo ra một cảnh báo để cho bạn biết rằng bạn đã có một cuộc đụng độ đặt tên ở đây. Bạn cần phải cung cấp từ khóa new
để loại bỏ nó.
Đây là một trường hợp tối nghĩa, nhưng nếu bạn muốn hỗ trợ kế thừa qua các ranh giới thành phần, bạn cần điều này, nếu không, việc bổ sung thành viên mới hoặc được bảo vệ trên lớp cơ sở sẽ luôn thay đổi. Đó là lý do tại sao điều này là ở đây. Nhưng đó là thực tế xấu bao giờ dựa vào nó, do đó thực tế là bạn nhận được một cảnh báo trình biên dịch. Việc sử dụng từ khóa new
để loại bỏ cảnh báo chỉ nên là một điểm dừng. Điểm mấu chốt là: tính năng này chỉ tồn tại vì một lý do duy nhất, và đó là giúp bạn thoát khỏi một lỗ hổng trong trường hợp phiên bản mới của một số lớp cơ sở đã thêm thành viên chưa từng tồn tại, và đụng độ với một thành viên đã có trong lớp dẫn xuất của bạn. Nếu đó không phải là tình huống bạn đang ở, không sử dụng tính năng này.
(Tôi nghĩ họ thực sự cần ban hành một lỗi chứ không phải là một cảnh báo khi bạn bỏ qua mới, để làm điều này rõ ràng hơn.)
Như những người khác đã nói, điều này không ghi đè - và nó * chính xác * như tôi mong đợi. Xin đừng cho rằng mọi người đều nghĩ giống như cách bạn làm. –