Dịch vụ nên ném ngoại lệ hoặc trả lại khi không có mục nào được chỉ định để xóa


12

Tôi có một đoạn mã có thể được biểu diễn dưới dạng:

public class ItemService {

    public void DeleteItems(IEnumerable<Item> items)
    {
        // Save us from possible NullReferenceException below.
        if(items == null)
            return;

        foreach(var item in items)
        {
            // For the purpose of this example, lets say I have to iterate over them.
            // Go to database and delete them.
        }
    }
}

Bây giờ tôi tự hỏi liệu đây là cách tiếp cận đúng hay tôi nên ném ngoại lệ. Tôi có thể tránh ngoại lệ, bởi vì trả về sẽ giống như lặp đi lặp lại một bộ sưu tập trống, có nghĩa là, không có mã quan trọng nào được thực thi, nhưng mặt khác tôi có thể ẩn các vấn đề ở đâu đó trong mã, bởi vì tại sao mọi người muốn gọi DeleteItemsvới nulltham số nào? Điều này có thể chỉ ra rằng có một vấn đề ở đâu đó trong mã.

Đây là một vấn đề tôi thường gặp phải với các phương thức trong dịch vụ, vì hầu hết chúng đều làm gì đó và không trả về kết quả, vì vậy nếu ai đó truyền thông tin không hợp lệ thì dịch vụ sẽ không có gì để làm, vì vậy nó sẽ trả về.


2
Đây chỉ là ý kiến ​​cá nhân của tôi, nhưng tôi luôn thấy nó có ý nghĩa nhất là một phương pháp nên đưa ra một ngoại lệ khi nó làm điều gì đó ngoài ý muốn (ngoại lệ). Trong trường hợp của bạn, tôi sẽ ném một UnlimitedOperationException nếu ai đó cố xóa các mục null / 0.
Falgantil


1
@gnat Câu hỏi của tôi là cụ thể về các dịch vụ, vì cách chúng được sử dụng và mục đích của chúng. "Bản sao" này chỉ nói về các trường hợp chung và câu trả lời chính thậm chí mâu thuẫn với chính nó trong bối cảnh phương pháp chính xác của tôi.
FCin

2
Có thể cho vô số để trống thay vì null? Bạn sẽ xử lý khác nhau?
JAD

13
@Facheantil Tôi có thể thấy null xứng đáng là một ngoại lệ, nhưng tôi nghĩ thật vô lý khi ném một ngoại lệ vào một danh sách trống.
Kevin

Câu trả lời:


58

Đây là hai câu hỏi khác nhau.

Bạn có nên chấp nhận null? Điều đó phụ thuộc vào chính sách chung của bạn về nullcơ sở mã. Theo tôi, cấm nullở mọi nơi trừ nơi được ghi chép rõ ràng là một cách làm rất tốt, nhưng thậm chí còn tốt hơn để tuân thủ quy ước mà cơ sở mã của bạn đã có.

Bạn có nên chấp nhận bộ sưu tập trống? Theo tôi: CÓ, hoàn toàn. Nỗ lực nhiều hơn để hạn chế tất cả người gọi vào các bộ sưu tập không trống hơn là làm điều đúng đắn về mặt toán học - ngay cả khi điều đó làm ngạc nhiên một số nhà phát triển là iffy với khái niệm về số không.


9
Trong mọi trường hợp, nếu bạn định ném, thì sẽ không có nhiều tiện ích như vậy AhaINoticedYouPassingInNullExceptionkhi thời gian chạy đã cung cấp cho bạn phương tiện ném NullReferenceExceptioncho bạn, mà không cần bạn viết bất kỳ mã nào cả. Có một số tiện ích (ví dụ: công cụ bao phủ mã của bạn sẽ cho bạn biết liệu bạn có kiểm tra đơn vị để chuyển thành null trong trường hợp trước không nhưng không phải là trường hợp sau), nhưng không nên ngoại lệ / thử trên mỗi cuộc gọi đến dịch vụ, bởi vì đó thường là lỗi lập trình viên.
Steve Jessop

2
... bạn chỉ nên ngay lập tức nắm bắt các ngoại lệ được nêu ra do các tham số sơ sài, nếu bạn biết rằng những gì bạn đang truyền vào là không có giá trị trong một số trường hợp dự kiến ​​và bạn chủ động muốn chức năng bạn gọi để xác thực nó cho bạn. Giống như Kilian nói trong câu trả lời, chính sách chung của bạn có thể là cấm null ở mọi nơi mà nó không được phép rõ ràng. Sau đó, bạn sẽ không bắt NullReferenceExceptionhoặc AhaINoticedYouPassingInNullExceptionbất cứ nơi nào ngoại trừ ở mã khôi phục thảm họa cấp cao. Và trình xử lý ngoại lệ đó có thể sẽ tạo một báo cáo lỗi :-)
Steve Jessop

2
@FCin: thiết kế giao diện của bạn phải phù hợp với những gì người dùng thực sự cần từ dịch vụ. Vì vậy, nếu mọi người gọi sẽ đặt thử / bắt xung quanh điều này, thì phương thức này hoặc phương thức tiện lợi nào đó bên cạnh nó nên kiểm tra và bỏ qua null, vì đó là những gì công chúng yêu cầu. Tuy nhiên, như Kilian nói, nó thường hoạt động tốt hơn khi coi việc truyền null là lỗi lập trình và không cố gắng tiếp tục tại mọi trang web cuộc gọi. Đối với vấn đề đó, điều gì sẽ xảy ra nếu dịch vụ mà phương thức này được gọi là null- các bộ điều khiển có chứa phần mềm để bắt và tiếp tục trong trường hợp đó không?
Steve Jessop

2
... Nếu vậy, thì có vẻ như cơ sở mã luôn xử lý các thành phần bị thiếu như một điều kiện dự kiến ​​mà bạn nên xử lý ở mức thấp và tiếp tục. Bạn có thể hỏi một cách hợp lý, "trách nhiệm của ai là cung cấp dịch vụ và vô số để hoạt động này có thể đi trước?". Nếu câu trả lời là "ai đó", thì chức năng của bạn đang kiểm tra các điều kiện tiên quyết và kết quả của một điều kiện tiên quyết thất bại thông thường sẽ là một ngoại lệ được bắt ở mức cao, không phải ở mỗi cuộc gọi. Nếu các công cụ cần thiết cho hoạt động là thực sự tùy chọn, thì chức năng của bạn đang xử lý một trường hợp sử dụng dự kiến .
Steve Jessop

2
Hoàn toàn ném một ArgumentNullExceptionưu thế hơn là cho phép mã bên trong ném NullReferenceException- hoàn toàn nhìn vào thông báo ngoại lệ, rõ ràng đó là lỗi đầu vào chứ không phải lỗi logic trong thân phương thức tại vị trí ném và đảm bảo thoát sớm có nghĩa là bạn có nhiều khả năng đã để các dữ liệu khác ở trạng thái hợp lệ thay vì bị đột biến một phần từ một null bất ngờ sau này.
Miral

9

Giá trị vô giá trị

Như @KilianFoth đã nói, hãy tuân thủ chính sách chung của bạn. Nếu đó là để coi nullnhư một "tốc ký" cho một danh sách trống, hãy làm theo cách đó.

Nếu bạn không có chính sách nhất quán về nullcác giá trị, tôi khuyên bạn nên sử dụng chính sách sau:

nullnên được dành riêng để đại diện cho các tình huống không thể được thể hiện bằng loại "bình thường", ví dụ như sử dụng nullđể đại diện cho "Tôi không biết". Và đó là một lựa chọn tốt, vì mọi người cố gắng sử dụng giá trị này một cách bất cẩn sẽ nhận được một ngoại lệ, đó là điều đúng đắn.

Sử dụng nullnhư một cách viết tắt cho một danh sách trống không đủ điều kiện theo cách đó, vì đã có một đại diện hoàn hảo, là một danh sách có các phần tử bằng không. Và đó là một lựa chọn tồi về mặt kỹ thuật, vì nó buộc mọi phần trong mã của bạn xử lý các danh sách để kiểm tra tốc ký hợp lệ null.

Danh sách trống

Đối với một DeleteItems()phương thức, vượt qua một danh sách trống có hiệu quả có nghĩa là không làm gì cả. Tôi cho phép điều đó như một cuộc tranh luận, không ném ra một ngoại lệ, chỉ cần quay lại nhanh chóng.

Tất nhiên, người gọi có thể kiểm tra các phần tử 0 trước và bỏ qua DeleteItems()cuộc gọi trong trường hợp đó. Nếu chúng ta đang nói về API web, vì lý do hiệu quả, người gọi thực sự nên làm điều đó để tránh lưu lượng truy cập không cần thiết và độ trễ chuyến đi khứ hồi. Nhưng tôi không nghĩ API của bạn nên thực thi điều đó.


1

Ném một ngoại lệ và xử lý null trong mã gọi.

Như một quy tắc thiết kế cố gắng tránh null làm giá trị tham số. Nó sẽ giảm NullPulumExceptions nói chung, vì null thực sự sẽ là một ngoại lệ.

Bên cạnh đó, hãy nhìn vào phần còn lại của mã của bạn. Nếu đây là một mô hình phổ biến trong dự án của bạn thì hãy kiên định.


Tôi đang ở giữa "định hình" cách các dịch vụ của tôi được cấu trúc, vì vậy một số cấu trúc lại có thể được yêu cầu để đưa vào đầu vào không hợp lệ, nhưng không nhiều. Nhưng tôi đồng ý với quan điểm của bạn rằng null sẽ trở thành một ngoại lệ khi tôi bắt đầu ném thay vì che giấu chúng.
FCin

0

Nói chung, ném ngoại lệ nên được dành riêng cho các tình huống đặc biệt, tức là nếu mã không có quá trình hành động hợp lý để thực hiện trong bối cảnh hiện tại.

Bạn có thể áp dụng quá trình suy nghĩ đó cho tình huống này. Cho rằng đây là lớp công khai với phương thức công khai, bạn có API công khai và trên lý thuyết không kiểm soát được những gì được truyền cho nó.

Mã của bạn không có ngữ cảnh về những gì đang gọi nó (vì nó không nên) và không có gì bạn có thể làm một cách hợp lý với giá trị null. Đây chắc chắn sẽ là một ứng cử viên cho một ArgumentNullException.


"Nếu mã không có quá trình hành động hợp lý để thực hiện trong bối cảnh hiện tại". Nhưng suy nghĩ của tôi là, nó có quá trình hành động hợp lý, đó là trả lại khoảng trống. Nó thực hiện chính xác giống như khi chuyển bộ sưu tập trống, vì vậy nếu tôi cho phép bộ sưu tập trống thì tại sao tôi không cho phép null.
FCin

1
@FCin Vì bộ sưu tập trống mà bạn biết nó trống. Với nullbạn không có bộ sưu tập. Nếu mã cuộc gọi được cho là / dự kiến ​​sẽ cung cấp một bộ sưu tập cho phương thức, có gì đó không đúng, vì vậy bạn nên ném. Lý do duy nhất để coi null giống như một bộ sưu tập trống là nếu bạn có thể mong đợi một cách hợp lý mã gọi để tạo ra các bộ sưu tập null. Như các câu trả lời khác lưu ý, hầu hết các lần, một cái gì đó nulllà một sự bất thường. Nếu bạn coi những thứ này giống như các bộ sưu tập trống, bạn có khả năng bỏ qua một vấn đề ở nơi khác trong mã.
JAD

@FCin: đồng thời, nếu bạn có nullnghĩa là trống rỗng, đó là bối cảnh cụ thể cho phương pháp này. Ở một nơi khác trong cùng một cơ sở mã, bạn có thể có một tham số IEnumerablehoặc IContainermột số loại lọc, nullcó thể có nghĩa là "không có bộ lọc" (cho phép mọi thứ) trong khi bộ lọc trống có nghĩa là không cho phép. Và ở một nơi khác, nullcó thể có nghĩa rõ ràng là "Tôi không biết". Vì vậy, cho rằng điều đó nullkhông phải lúc nào cũng có nghĩa hoàn toàn giống nhau ở mọi nơi, đó là một ý tưởng tốt để không phát minh ra bất kỳ ý nghĩa nào cho nó trừ khi ý nghĩa đó là cần thiết hoặc ít nhất là hữu ích cho ai đó.
Steve Jessop

0

Câu hỏi này dường như không phải là về ngoại lệ, mà là về nullmột đối số hợp lệ.

Vì vậy, trước tiên, bạn phải quyết định xem có phải nulllà giá trị được phép cho đối số của phương thức đó hay không. Nếu có, thì bạn không cần một ngoại lệ. Nếu không, thì bạn cần một ngoại lệ.

Cho dù bạn có muốn cho phép nullhay không, vẫn còn gây tranh cãi, như được thể hiện bởi rất nhiều lượt truy cập của Google về điều đó. Có nghĩa là, bạn sẽ không nhận được câu trả lời rõ ràng, và điều đó phần nào phụ thuộc vào ý kiến ​​và truyền thống tại nơi bạn đang làm việc.

Một lĩnh vực khác của sự tranh cãi là liệu một chức năng thư viện có nên thực sự nghiêm ngặt về những điều như vậy hay nhẹ nhàng nhất có thể, miễn là nó không cố gắng "sửa chữa" các tham số sai lầm. So sánh điều này với thế giới của các giao thức mạng, chuyển thư, vv (là hợp đồng giao diện, giống như các phương thức trong lập trình). Thông thường, chính sách là người gửi nên tuân thủ nghiêm ngặt nhất có thể với giao thức, trong khi người nhận nên đi ra ngoài để có thể làm việc với bất cứ điều gì xảy ra.

Vì vậy, bạn phải quyết định: Có thực sự là công việc của một phương pháp thư viện để thực thi chính sách khá lớn như nullxử lý? Đặc biệt là nếu thư viện của bạn cũng được sử dụng bởi những người khác (những người có thể có các chính sách khác nhau).

Tôi có thể sẽ sai lầm khi cho phép nullcác giá trị, xác định và ghi lại ngữ nghĩa của chúng (nghĩa là null = empty arraytrong trường hợp này) và không đưa ra một ngoại lệ, trừ khi 99% mã tương tự khác của bạn (mã kiểu "thư viện") làm theo cách khác 'tròn.


1
"Đây có thực sự là công việc của một phương pháp thư viện để thực thi chính sách khá lớn như xử lý null?" - xuống dòng suy nghĩ nói dối khẳng định và chế độ gỡ lỗi, cho phép bạn giúp thực thi chính sách, thay vì thực sự thực thi nó, nếu đó là điều bạn muốn làm. Vì vậy, nếu bạn sẽ nuốt nulltheo nguyên tắc tự do trong những gì bạn chấp nhận, thì việc đăng nhập hoặc thậm chí ném sẽ hữu ích cho những người có ý định nghiêm khắc với những gì họ truyền vào, nhưng họ đã làm rối tung cả mã của họ và đơn vị của họ kiểm tra đến mức mà họ vượt qua null.
Steve Jessop

@SteveJessop, tôi thực sự không biết liệu bạn có đang tranh cãi với tinh thần của câu trả lời của tôi không, hoặc liệu bạn có đang tiếp tục theo dòng của nó không. Điều đó nói rằng, tôi không đề nghị đơn giản là nuốt null, nhưng tuyên bố công khai trong hợp đồng của phương pháp mà nó có thể chấp nhận được (hoặc không, tùy thuộc vào bất kỳ giải pháp nào mà OP đưa ra), và sau đó là câu hỏi có nên ném ngoại lệ không (hoặc không) tự giải quyết.
AnoE
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.