Khi nào và tại sao bạn sẽ niêm phong một lớp học?


87

Trong C # và C ++ / CLI, từ khóa sealed(hoặc NotInheritabletrong VB) được sử dụng để bảo vệ một lớp khỏi bất kỳ cơ hội kế thừa nào (lớp sẽ không thể kế thừa). Tôi biết rằng một tính năng của lập trình hướng đối tượng là tính kế thừa và tôi cảm thấy rằng việc sử dụng sealedtính năng này đi ngược lại tính năng này, nó sẽ ngừng kế thừa. Có ví dụ nào cho thấy lợi ích của sealedviệc sử dụng nó khi nào và quan trọng không?

Câu trả lời:


96
  1. Trên một lớp thực hiện các tính năng bảo mật, để đối tượng gốc không thể bị "mạo danh".

  2. Nói chung hơn, gần đây tôi đã trao đổi với một người ở Microsoft, người này nói với tôi rằng họ đã cố gắng giới hạn tính kế thừa ở những nơi mà nó thực sự có ý nghĩa, vì nó sẽ trở nên đắt đỏ về hiệu suất nếu không được điều trị.
    Từ khóa được niêm phong cho CLR biết rằng không có lớp nào ở bên dưới để tìm kiếm các phương thức và điều đó làm tăng tốc độ.

Trong hầu hết các công cụ nâng cao hiệu suất trên thị trường hiện nay, bạn sẽ tìm thấy một hộp kiểm sẽ đóng dấu tất cả các lớp không được kế thừa của bạn.
Tuy nhiên, hãy cẩn thận, bởi vì nếu bạn muốn cho phép các plugin hoặc cài đặt khám phá thông qua MEF, bạn sẽ gặp phải sự cố.


3
Ý tôi là hãy cẩn thận với việc niêm phong các lớp trong các thư viện được sử dụng lại, đặc biệt nếu chúng được các bên thứ ba sử dụng lại và sau đó được tích hợp lại (thông qua MEF) vào cơ sở mã. Cơ sở mã của bạn có thể không kế thừa một lớp nhất định nhưng các bên thứ ba sẽ làm.
Louis Kottmann

10
Lý do số 1 nghe có vẻ mơ hồ nhưng, giả sử chúng ta không viết "các tính năng bảo mật" hầu hết thời gian, điều đó có nghĩa là lý do số 1 hầu như không được áp dụng? Lý do số 2 là để điều chỉnh hiệu suất. Chúng ta đang nói đến sự khác biệt về hiệu suất là bao nhiêu? Chúng có đủ quan trọng để biện minh cho việc thay đổi định nghĩa của lớp không bảo mật không? Ngay cả khi câu trả lời là "có", thì đây lý tưởng sẽ là một tùy chọn trình biên dịch tức là "tạo mã được tối ưu hóa cho tất cả các lớp không được niêm phong", thay vì yêu cầu các nhà phát triển thay đổi cơ sở mã.
RayLuo

1
nó trở nên đắt đỏ về hiệu suất nếu không được điều trị, liệu điều này có thể đo lường được với số lượng thử nghiệm điên rồ ít hơn không?
t3chb0t

4
Niêm phong hút. Nó làm cho việc kiểm tra khó hơn - Tôi muốn chế nhạo một vài lớp ASP.NET với FakeItEasy, nhưng tôi không thể vì chúng được niêm phong.
Tinh tinh hiếu chiến vào

2
Không thể đồng ý hơn với @RayLuo. Tôi đã nhấn mạnh vài lần rằng mọi người đã niêm phong các lớp học của họ, nơi bảo mật và hiệu suất không thực sự là vấn đề. "Phong ấn" của họ chỉ đơn giản là ngăn cản nhu cầu hợp lý của tôi về việc ghi đè các lớp học, khiến mọi thứ trở nên khó khăn hơn nhiều. Giống như Warlike Chimpanzee đã nói, chế nhạo một lớp học là điều quá phổ biến trong quá trình thử nghiệm.
ZZY

15

Một phụ lục cho câu trả lời xuất sắc của Baboon :

  1. Nếu một lớp không được thiết kế để kế thừa, các lớp con có thể phá vỡ các bất biến của lớp . Điều này thực sự chỉ áp dụng nếu bạn đang tạo một API công khai, tất nhiên, nhưng theo nguyên tắc chung, tôi niêm phong bất kỳ lớp nào không được thiết kế rõ ràng để trở thành lớp con.

Trên một lưu ý liên quan, chỉ áp dụng cho các lớp chưa được niêm phong: bất kỳ phương thức nào được tạo virtualđều là một điểm mở rộng hoặc ít nhất có vẻ như nó phải là một điểm mở rộng. Khai báo các phương thức virtualcũng nên là một quyết định có ý thức. (Trong C #, đây là một quyết định có ý thức; trong Java thì không.)


CHỈNH SỬA : Một số liên kết có liên quan:

Cũng lưu ý rằng Kotlin niêm phong các lớp theo mặc định; từ khóa của nó openđối lập với Java finalhoặc sealedC # . (Để chắc chắn, không có thỏa thuận chung nào rằng đây là một điều tốt .)


25
Các lớp niêm phong gây đau đầu hơn là có lợi. Tôi đã liên tục tìm thấy các tình huống trong đó các nhà phát triển có các lớp bị niêm phong, khiến tôi gặp khó khăn hàng giờ trong việc tìm kiếm những gì nên đơn giản. Hãy ngừng niêm phong lớp học, bạn không hóm hỉnh như bạn nghĩ đâu. Chỉ niêm phong các lớp nếu bạn PHẢI, và thậm chí sau đó, hãy xem xét lại. Chỉ là ý kiến ​​của tôi, với tư cách là một chàng trai phải đối mặt với các lớp phong ấn của người khác mà tôi không thể chỉnh sửa / hủy niêm phong.
Gant Laborde

9
Bình luận của @GantMan thực sự nên được coi là một trong những câu trả lời cho câu hỏi của OP, vì về cơ bản nó đưa ra câu trả lời là "Khi nào? Khó. Tại sao? Đây là lý do tại sao bạn KHÔNG làm điều đó." Cho rằng bạn nên đăng lại bình luận của mình dưới dạng một câu trả lời riêng biệt và sau đó thu thập phiếu bầu cho nó. :-)
RayLuo

1
Đây có phải là câu trả lời cho câu trả lời này: stackoverflow.com/a/7777674/3195477 không? Liên kết với nó tốt hơn là (chỉ) đặt tên cho người đó
UuDdLrLrSs

2

Đánh dấu một lớp để Sealedngăn chặn việc giả mạo các lớp quan trọng có thể ảnh hưởng đến bảo mật hoặc ảnh hưởng đến hiệu suất.

Nhiều khi, việc niêm phong một lớp cũng có ý nghĩa khi một lớp đang thiết kế một lớp tiện ích với hành vi cố định, mà chúng ta không muốn thay đổi.

Ví dụ, Systemkhông gian tên trong C#cung cấp nhiều lớp được niêm phong, chẳng hạn như String. Nếu không được niêm phong, có thể mở rộng chức năng của nó, điều này có thể không mong muốn, vì đây là loại cơ bản với chức năng nhất định.

Tương tự, structurestrong C#luôn luôn được niêm phong ngầm. Do đó người ta không thể lấy một cấu trúc / lớp từ cấu trúc khác. Lý do cho điều này là chỉ structuresđược sử dụng để lập mô hình các kiểu dữ liệu độc lập, nguyên tử, do người dùng xác định , mà chúng tôi không muốn sửa đổi.

Đôi khi, khi bạn đang xây dựng cấu trúc phân cấp lớp, bạn có thể muốn giới hạn một nhánh nhất định trong chuỗi kế thừa, dựa trên mô hình miền hoặc quy tắc kinh doanh của bạn.

Ví dụ: a ManagerPartTimeEmployeeđều là Employees, nhưng bạn không có bất kỳ vai trò nào sau nhân viên bán thời gian trong tổ chức của mình. Trong trường hợp này, bạn có thể muốn niêm phong PartTimeEmployeeđể ngăn phân nhánh thêm. Mặt khác, nếu bạn có nhân viên bán thời gian hàng giờ hoặc hàng tuần, bạn nên kế thừa họ từ đó PartTimeEmployee.


Làm thế nào để mở rộng lớp String là không mong muốn? Chuỗi sẽ vẫn hoạt động chính xác như hiện tại và bạn có thể có một lớp dẫn xuất với chức năng bổ sung khi điều đó được mong muốn, vậy bạn đang nói về vấn đề gì?
Kevin Wells

Ngoài ra, điểm "giới hạn" hệ thống phân cấp thừa kế của bạn sẽ là gì? Nó có nghĩa là nếu bạn đã bao giờ đã làm nhu cầu mở rộng hệ thống phân cấp mà bạn sẽ phải mở niêm phong các tầng lớp phụ huynh đầu tiên, mà chỉ là không hiệu quả
Kevin Wells

Hãy xem bài đăng xuất sắc này của Eric Lippert và câu hỏi SO này .
Akshay Khot

1
Ngay cả câu trả lời đó về cơ bản cũng tóm tắt thành "Tại sao bạn muốn lấy String?", Sau đó tiếp tục đề cập đến lý do tại sao bạn có thể muốn lấy String (ví dụ: chuỗi bị kết thúc bằng null) và nói rằng bạn chỉ nên làm việc xung quanh nó mà không cần kế thừa. Vậy tại sao làm cho nó phức tạp hơn và phải làm việc xung quanh nó sau khi bạn chỉ có thể rời khỏi nó niêm phong ở nơi đầu tiên và để lại tùy chọn của bạn mở
Kevin Wells

Đối với câu hỏi thứ hai, mục tiêu sẽ là ngăn chặn hành vi không mong muốn (tùy thuộc vào logic nghiệp vụ). unsealLớp sau này sẽ dễ dàng hơn , nếu cần, thay vì niêm phong nó và phá vỡ tất cả các lớp phụ thuộc vào nó.
Akshay Khot

0

Tôi nghĩ rằng bài đăng này có một số điểm tốt, trường hợp cụ thể là khi cố gắng truyền một lớp không niêm phong đến bất kỳ giao diện ngẫu nhiên nào, trình biên dịch không gây ra lỗi; nhưng khi niêm phong được sử dụng, trình biên dịch sẽ ném ra lỗi rằng nó không thể chuyển đổi. Lớp kín mang lại bảo mật truy cập mã bổ sung.
https://www.codeproject.com/Articles/239939/Csharp-Tweaks-Why-to-use-the-sealed-keyword-on-cla


1
Liên kết đến một giải pháp được hoan nghênh, nhưng hãy đảm bảo rằng câu trả lời của bạn hữu ích nếu không có nó: thêm ngữ cảnh xung quanh liên kết để những người dùng đồng nghiệp của bạn sẽ biết nó là gì và tại sao nó ở đó, sau đó trích dẫn phần có liên quan nhất của trang bạn ' đang liên kết lại trong trường hợp trang đích không có sẵn. Các câu trả lời ít hơn một liên kết có thể bị xóa.
Baum mit Augen

Xin lỗi, tôi không định đăng nó dưới dạng câu trả lời, nhưng có vẻ như nó không liên quan đến các câu trả lời khác và tôi không biết phải đặt nó ở đâu
strisunshine

1
Tôi đã sửa bài theo gợi ý. Ban đầu tôi chỉ muốn đóng góp một góc độ khác (có lẽ), nhưng tôi chỉ nhận được một phản đối và chúng tôi chưa nói về nội dung, -1 có thể vui lòng cho biết lý do được không?
strisunshine
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.