Là ném một ngoại lệ từ một hình thức xấu tài sản?


14

Tôi luôn có suy nghĩ rằng các thuộc tính (nghĩa là các hoạt động set / get của chúng) phải nhanh / tức thời và không bị lỗi. Bạn không bao giờ phải cố gắng / nắm bắt xung quanh việc nhận hoặc thiết lập một tài sản.

Nhưng tôi đang xem xét một số cách để áp dụng bảo mật dựa trên vai trò trên các thuộc tính của một số đối tượng. Ví dụ, một nhân viên. Tài sản chung. Một số giải pháp tôi đã chạy qua mà các giải pháp khác đã thử (cụ thể là ví dụ AOP ở đây ) liên quan đến việc ném ngoại lệ nếu người truy cập không có quyền phù hợp - nhưng điều này đi ngược lại quy tắc cá nhân mà tôi đã có trong một thời gian dài bây giờ

Vậy tôi hỏi: tôi có sai không? Mọi thứ đã thay đổi? Nó đã được chấp nhận rằng các tài sản sẽ có thể ném ngoại lệ?


1
Đây cùng một câu hỏi về StackOverflow đã chú ý nhiều hơn và câu trả lời rất tốt.
Roman Starkov

Câu trả lời:


14

khi bạn đặt giá trị của một thuộc tính, ném ngoại lệ vào giá trị không hợp lệ là ổn

nhận được giá trị của một tài sản nên (hầu như) không bao giờ ném ngoại lệ

để truy cập dựa trên vai trò, sử dụng các giao diện hoặc mặt tiền khác nhau; đừng để mọi người thấy những thứ họ không thể có!


5

Tôi coi nó chủ yếu là hình thức xấu nhưng thậm chí Microsoft khuyên bạn nên sử dụng ngoại lệ đôi khi. Một ví dụ điển hình là thuộc tính trừu tượng Stream.Lipse . Theo hướng dẫn, tôi sẽ quan tâm nhiều hơn đến việc tránh tác dụng phụ đối với getters và hạn chế tác dụng phụ trên setters.


4

Tôi chắc chắn sẽ lập luận rằng có một lỗ hổng trong thiết kế nếu bạn cảm thấy cần phải ném ngoại lệ từ một trình thiết lập hoặc getter thuộc tính.

Một tài sản là một sự trừu tượng đại diện cho một cái gì đó chỉ là một giá trị . Và bạn sẽ có thể đặt giá trị mà không sợ rằng làm như vậy có thể tạo ra ngoại lệ. *

Nếu thiết lập các kết quả thuộc tính trong một hiệu ứng phụ, điều đó thực sự nên được thực hiện như một phương pháp thay thế. Và nếu nó không tạo ra bất kỳ tác dụng phụ nào, thì không nên ném ngoại lệ.

Một ví dụ đã được đề cập trong một câu trả lời khác nhau là Stream.Positiontài sản. Điều này không tạo ra tác dụng phụ, và có thể ném ngoại lệ. Nhưng trình thiết lập thuộc tính này về cơ bản chỉ là một trình bao bọc Stream.Seekmà bạn có thể gọi thay thế.

Cá nhân, tôi tin rằng vị trí không nên là một tài sản có thể ghi.

Một ví dụ khác mà bạn có thể muốn đưa ra một ngoại lệ từ trình thiết lập thuộc tính là trong việc xác thực dữ liệu:

public class User {
    public string Email {
        get { return _email; }
        set { 
            if (!IsValidEmail(value)) throw InvalidEmailException(value);
            _email = value;
        }
    }

Nhưng có một giải pháp tốt hơn cho vấn đề này. Giới thiệu một loại đại diện cho một địa chỉ email hợp lệ:

public class Email {
    public Email(string value) {
        if (!IsValidEmail(value)) throw new InvalidEmailException(value);
        ...
    }
    ...
}

public class User {
    public Email Email { get; set; }
}

Các EmailĐảm bảo lớp mà nó không thể giữ một giá trị không phải là một địa chỉ email hợp lệ, và các lớp học mà cần phải email lưu trữ được giải phóng khỏi nhiệm vụ chứng thực họ.

Điều này cũng dẫn đến sự gắn kết cao hơn (một chỉ số về thiết kế phần mềm tốt) - kiến ​​thức về địa chỉ email là gì và cách xác thực, chỉ tồn tại trong Emaillớp, chỉ có mối quan tâm đó.

* ObjectDisposedException là ngoại lệ hợp lệ duy nhất (không có ý định chơi chữ) tôi có thể nghĩ đến vào lúc này.


2

Tôi biết câu hỏi của bạn là dành riêng cho .NET , nhưng vì C # chia sẻ một số lịch sử với Java, tôi nghĩ bạn có thể quan tâm. Tôi không có nghĩa là vì điều gì đó được thực hiện trong Java, nên nó được thực hiện trong C #. Tôi biết cả hai rất khác nhau, đặc biệt là cách C # có hỗ trợ cấp độ ngôn ngữ được cải thiện nhiều cho các thuộc tính. Tôi chỉ đưa ra một số bối cảnh và quan điểm.

Từ đặc tả JavaBeans :

Các thuộc tính bị ràng buộc Đôi khi khi một thay đổi thuộc tính xảy ra, một số bean khác có thể muốn xác thực thay đổi và từ chối nó nếu nó không phù hợp. Chúng tôi đề cập đến các thuộc tính trải qua loại kiểm tra này như các thuộc tính bị ràng buộc. Trong Đậu Java, các phương thức thiết lập thuộc tính bị ràng buộc là bắt buộc để hỗ trợ PropertyVetoException. Tài liệu này cho người dùng của tài sản bị ràng buộc đã cố cập nhật có thể bị từ chối. Vì vậy, một thuộc tính ràng buộc đơn giản có thể trông giống như:

PropertyType getFoo();
void setFoo(PropertyType value) throws PropertyVetoException;

Vui lòng lấy tất cả những thứ này với một hạt muối. Đặc tả JavaBeans đã cũ và Thuộc tính C # (IMO) là một cải tiến lớn so với các thuộc tính dựa trên "quy ước đặt tên" mà Java có. Tôi chỉ đang cố gắng đưa ra một bối cảnh nhỏ, là tất cả!


Đó là một điểm tốt, nhưng sự khác biệt giữa phương thức setter và set set thuộc tính ac # đủ quan trọng để trở thành một trường hợp khác với tôi. Tương tự, các ngoại lệ được kiểm tra của Java buộc mã gọi phải nhận thức được rằng có thể ném ngoại lệ, do đó, ít có khả năng điều này sẽ khiến bất kỳ ai ngạc nhiên.
Steven Evers

2

Điểm của một thuộc tính là Nguyên tắc truy cập thống nhất , nghĩa là một giá trị phải được truy cập thông qua cùng một giao diện cho dù được thực hiện bằng cách lưu trữ hoặc tính toán. Nếu thuộc tính của bạn đang đưa ra một ngoại lệ thể hiện một điều kiện lỗi nằm ngoài sự kiểm soát của lập trình viên, loại được cho là bị bắt và xử lý, bạn đang buộc khách hàng của mình biết rằng giá trị thu được thông qua tính toán.

Mặt khác, tôi không thấy bất kỳ vấn đề nào khi sử dụng các xác nhận hoặc các ngoại lệ giống như khẳng định nhằm báo hiệu việc sử dụng API không chính xác cho lập trình viên thay vì bị bắt và xử lý. Trong những trường hợp này, câu trả lời chính xác từ phối cảnh của người dùng API sẽ không xử lý ngoại lệ (do đó hoàn toàn quan tâm đến việc liệu giá trị thu được thông qua tính toán hay lưu trữ). Đó là để sửa mã của anh ấy / cô ấy để đối tượng không kết thúc ở trạng thái không hợp lệ hoặc khiến bạn sửa mã của mình để xác nhận không kích hoạt.


Tôi không thấy cách mà bạn đang buộc khách hàng của mình biết rằng giá trị thu được thông qua tính toán mà sau đây. Chắc chắn truy cập vào lưu trữ cũng có thể ném một ngoại lệ. Trong thực tế, làm thế nào bạn thậm chí sẽ xác định sự khác biệt giữa hai?
Timwi

Tôi tin rằng sự khác biệt được tạo ra là chỉ cần lấy một giá trị từ một trường (lưu trữ) và làm một cái gì đó vô hại với nó, chẳng hạn như đặt nó vào một biến của một loại thích hợp (nghĩa là không truyền), không thể ném ngoại lệ. Trong trường hợp đó, một ngoại lệ chỉ có thể xảy ra nếu sau đó bạn đã làm gì đó với giá trị. Nếu một ngoại lệ được ném vào một getter thuộc tính, thì trong trường hợp đó, chỉ cần hành động lấy giá trị và đặt nó vào một biến có thể gây ra ngoại lệ.
Học viên của Tiến sĩ Wily

1

AFAIK, hướng dẫn này chủ yếu xuất phát từ quá trình suy nghĩ rằng các thuộc tính có thể sẽ được sử dụng tại thời điểm thiết kế . (Ví dụ: thuộc tính Text trên TextBox) Nếu thuộc tính ném ngoại lệ khi nhà thiết kế cố gắng truy cập vào nó, VS sẽ không có một ngày tốt. Bạn sẽ nhận được một loạt lỗi khi cố gắng sử dụng trình thiết kế và đối với các nhà thiết kế UI, chúng sẽ không hiển thị. Nó cũng áp dụng cho thời gian gỡ lỗi, mặc dù những gì bạn sẽ thấy trong một trường hợp ngoại lệ chỉ là "xxx Exception" và nó sẽ không bị chặn VS IIRC.

Đối với POCO, nó thực sự sẽ không gây hại gì, nhưng tôi vẫn ngại tự mình làm điều đó. Tôi cho rằng mọi người sẽ muốn truy cập các tài sản thường xuyên hơn, vì vậy chúng thường có chi phí thấp. Các thuộc tính không nên thực hiện công việc của các phương thức, chúng chỉ nên lấy / đặt một số thông tin và được thực hiện với nó như một quy tắc chung.


0

Mặc dù rất nhiều thứ sẽ phụ thuộc vào bối cảnh mà tôi thường tránh đặt bất kỳ loại logic có thể phá vỡ nào (bao gồm các ngoại lệ) vào các thuộc tính. Cũng giống như một ví dụ, có rất nhiều thứ mà bạn (hoặc một số thư viện mà bạn đang sử dụng) có thể thực hiện bằng cách sử dụng sự phản chiếu để lặp lại các thuộc tính dẫn đến lỗi mà bạn không lường trước được trong thời gian thiết kế.

Tôi nói chung mặc dù có thể đôi khi bạn chắc chắn, chắc chắn, muốn chặn một cái gì đó mà không phải lo lắng quá nhiều về hậu quả. An ninh tôi đoán sẽ là trường hợp cổ điển ở đó.

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.