Kiểm tra kết quả của hàm tạo trong C #


8

Tôi đang làm việc trên cơ sở mã với một đồng nghiệp có thói quen kiểm tra kết quả của một nhà xây dựng để tìm null trong một kiểu tương tự như thế này

Person p = new Person();
if (p != null)
{
    p.Name = "John Smith";
}

Sự hiểu biết của tôi về cảnh quan .NET là một nhà xây dựng sẽ không bao giờ để một nhiệm vụ không được thực hiện trừ khi có một ngoại lệ được đưa ra. Vì vậy, trong trường hợp trên, kiểm tra null là vô ích. Hoặc pđược phân bổ, hoặc một ngoại lệ sẽ được ném làm cho trình thiết lập thuộc tính bị bỏ qua.

Tôi đã hỏi đồng nghiệp của mình về vấn đề này và tôi nhận được câu trả lời thụ động dọc theo dòng chữ "chỉ trong trường hợp". Bản thân tôi không thích kiểu "lập trình paronia" này. Tôi nghĩ rằng nó làm tổn thương khả năng đọc và không cần thiết làm tăng độ phức tạp chu kỳ. Vì điều này, tôi muốn đưa ra một yêu cầu chính thức rằng việc thực hành này phải được dừng lại. Đây có phải là một yêu cầu hợp lý? Tui bỏ lỡ điều gì vậy?



1
Có vẻ như nó phải là một cảnh báo trình biên dịch ở mức tối thiểu hoặc cảnh báo phân tích mã.
Frank Hileman

@FrankHileman - điểm hay ... trình biên dịch C # có cảnh báo (0161) về mã không thể truy cập, mặc dù tôi không chắc chắn 100% có thể phát hiện trường hợp cụ thể này.
Jules

nó thực sự là một thông lệ tiêu chuẩn để kiểm tra, hay nó chỉ còn sót lại từ phiên bản trước? mà có thể có p = Rep Storage.Get (123) hoặc thứ gì đó
Ewan

Câu trả lời:


12

Tôi đồng ý với @MartinMaat về việc chọn các trận đánh của bạn.

Có nhiều trường hợp "chỉ trong trường hợp" do không thực sự hiểu ngôn ngữ mặc dù ngôn ngữ đã được cố định trong các quy tắc của nó đối với nhiều điều này - qua việc ngoặc đơn một biểu thức không cần nó do không hiểu các quy tắc ưu tiên của ngôn ngữ. Tuy nhiên, thực tế như vậy chủ yếu là vô hại.

Khi tôi còn trẻ, tôi cảm thấy rằng chúng ta nên tìm hiểu các chi tiết của ngôn ngữ và do đó tránh viết mã không cần thiết như vậy. (Một trong những thú cưng của tôi-Peeves là return (0);với dấu ngoặc không cần thiết của nó.) Tuy nhiên, bây giờ tôi vừa vị trí đó, đặc biệt, bởi vì chúng tôi sử dụng rất nhiều ngôn ngữ khác nhau bây giờ, nhảy từ client tới server, vv ... Vì vậy, bây giờ tôi cắt một số chùng cho một số vấn đề như vậy.

Bạn đang nói về cyclomatic bắt đầu đi đến tranh luận hợp lý. Hãy xem Bảo hiểm Mã và đặc biệt là mức độ bao phủ cao hơn :

  1. Mỗi quyết định có mọi kết quả có thể

Vì chúng tôi không thể buộc hoạt động mới trả về NULL, không có cách nào để đạt được mức độ bao phủ mã cao hơn cho hoạt động có điều kiện này.   Tất nhiên, điều này có thể hoặc không quan trọng đối với tổ chức của bạn!

Tuy nhiên, vì vấn đề bảo hiểm mã này, tôi sẽ ưu tiên nó cao hơn so với quá ngoặc đơn.

Mặt khác, mã được tạo bên dưới có thể sẽ không chịu một chút nào cho điều này vì các thế hệ mã, JIT và trình tối ưu hóa đều hiểu rằng newgiá trị ed sẽ không bao giờ là null. Vì vậy, chi phí thực tế chỉ đến về khả năng đọc và khả năng bao phủ mã nguồn.

Tôi sẽ hỏi bạn "phần khác" của câu lệnh if như thế nào?

Nếu không có phần nào khác, tôi sẽ lập luận rằng chỉ cần rơi khỏi phần cuối của thói quen hoặc chuyển sang mã khác (nghĩa là không có elsephần này if) là rất nguy hiểm, vì bây giờ điều này "chỉ trong trường hợp" gợi ý rằng người gọi và / hoặc thêm mã xuống dòng xử lý NULL là tốt.

Nếu nó đọc:

p = new Object ();
if ( p != null ) {
    p.field = value;
}
else {
    throw new NullReferenceException ();
}

thì điều này thực sự quá mức cần thiết, vì ngôn ngữ làm tất cả những điều đó cho chúng ta.

Tôi có thể đề nghị đảo ngược ý nghĩa của điều kiện - có lẽ đồng nghiệp của bạn sẽ thoải mái hơn với điều này:

p = new Object ();
if ( p == null ) {
    throw new NullReferenceException ();
}
else {
    p.field = value;
}

Bây giờ bạn có thể tranh luận về việc loại bỏ trình bao bọc khác, vì nó rất rõ ràng không cần thiết:

p = new Object ();
if ( p == null ) {
    throw new NullReferenceException ();
}
p.field = value;

Với điều này, "chỉ trong trường hợp" bây giờ là những gì có điều kiện, trong khi mã thành công thì không. Cách tiếp cận này củng cố thêm rằng khi phân bổ thất bại, phản hồi thích hợp sẽ được đưa ra, thay vì tiếp tục chạy mã trong phương thức này và / hoặc trong chuỗi cuộc gọi này (mà không có bất kỳ xử lý thích hợp nào khác đối với lỗi phân bổ).

Vì vậy, tóm lại, có hai lý lẽ hợp lý để đưa ra ở đây chống lại thực tiễn này:

  1. Mức độ bao phủ mã cao hơn không thể đạt được vì chúng ta không thể buộc hết bộ nhớ (hoặc bất kỳ lỗi xây dựng nào) để trả về null.
  2. "Chỉ trong trường hợp" (như được hiển thị ở trên trong câu hỏi) là không đầy đủ và như vậy là thiếu sót vì sự không nhất quán trong kỳ vọng về cách null được xử lý bởi các mã khác ngoài / quá khứ p.field = value;.

Về cơ bản, có vẻ như có lẽ đồng nghiệp của bạn đang ở trong hàng rào về việc sử dụng các ngoại lệ - mặc dù C # không có lựa chọn nào ở đây cho những điều như vậy. ( Nếu chúng tôi muốn mã được kiểm tra tốt, chúng tôi không thể mã cho cả mô hình ngoại lệ để xử lý null và mô hình không ngoại lệ bằng cách sử dụng giá trị null-return-side, side-side.) Có lẽ nếu bạn lý luận với đồng nghiệp của mình thông qua các chủ đề này, họ sẽ thấy một chút ánh sáng!


Bạn đánh vào đầu đinh liên quan đến đồng nghiệp của tôi đang ở trên hàng rào về các trường hợp ngoại lệ. Đối với bạn câu hỏi, không có elseđiều kiện trong ví dụ của tôi. Những gì họ sẽ làm chỉ là bỏ qua công việc nếu giá trị là null. Và kiểm tra null sẽ được phổ biến nếu trả lại thể hiện được phân bổ. Nó có mùi như mã lỗi, phải không? Tôi không thấy trường hợp này liên quan đến vấn đề đó, nhưng bạn đã chỉ ra một cách chính xác rằng chúng có liên quan rất nhiều. Cảm ơn đã phản hồi toàn diện. Các vấn đề bảo hiểm mã là một mất đi tốt cho tôi.
Jason Tyler

Đúng vậy, vấn đề là chúng ta không thể mã cho các trường hợp ngoại lệ mã lỗi ở cùng một nơi và cả hai đều có thể kiểm tra được! Nếu họ thực sự muốn mã lỗi, họ có thể bao bọc các newhoạt động trong một lần thử / bắt và cung cấp null và / hoặc mã lỗi cho ngoại lệ ngoài bộ nhớ - chuyển đổi ngoại lệ của C # cho OOM thành kiểu mã lỗi.
Erik Eidt

1
Mã ném ngoại lệ có thể được rút ngắn hơn nữa p = new Object() ?? throw new NullReferenceException();mà có lẽ sẽ khiến nó trở thành ứng cử viên rõ ràng hơn để tối ưu hóa?
wonderra

1
Có một số miền nhất định trong đó mã chết được coi là một trách nhiệm rất lớn đến nỗi nó bị nghiêm cấm, FWIW.
RubberDuck

8

Có lẽ bạn có thể né tránh vấn đề và chỉ cần cho anh ta biết rằng có một cú pháp c # mới hơn dễ dàng hơn (và kiểm tra null cho bạn!)

Thay vì

Person p = new Person();
if (p != null)
{
    p.Name = "John Smith";
}

Viết

var p = new Person
{
    Name = "John Smith"
};

2
Trong khi điều đó giải quyết mã ví dụ, nó không tự động áp dụng cho mọi trường hợp tương tự. Thay thế cơ thể của khối if var foo = p.DoSomething()và bạn sẽ thấy câu trả lời của mình không áp dụng nữa.
Flater

Đó là một gợi ý tốt và có thể giúp dọn dẹp một số trường hợp tôi đang giải quyết. Thật không may, ví dụ của tôi là một trường hợp đơn giản hóa của một vấn đề có nhiều mùi vị khác nhau, vì vậy điều này sẽ không loại bỏ vấn đề nói chung. Không thể làm tổn thương!
Jason Tyler

1
@Flater Người pranoid trong câu hỏi sau đó có thể đi với: var foo = p? .DoSthing ();
Eternal21

4

Đó là một yêu cầu hợp lý nhưng có vẻ như điều này đã trở thành một cuộc đấu tranh giữa bạn và anh ấy. Bạn có thể đã chỉ ra cho anh ta, anh ta miễn cưỡng thay đổi nó và bây giờ bạn có kế hoạch leo thang vấn đề. Sau đó, bạn có thể "chiến thắng".

Có thể sẽ dễ dàng hơn khi chỉ cần gửi cho anh ấy liên kết Robert Harvey đã đăng và nói "Tôi đã gửi cho bạn một liên kết về việc xây dựng đối tượng C # mà bạn có thể thấy thú vị" và để nó ở đó. Nó không chính xác có hại những gì anh ta làm (chỉ là vô nghĩa) và dễ dàng để làm sạch op sau này. Tôi đang nói: chọn trận chiến của bạn. Tôi đã ở trong tình huống này hơn một lần và khi nhìn lại, thường thì nó không đáng để đấu tranh. Tất cả các bạn quá dễ dàng để ghét nhau hơn hầu như không có gì. Vâng, bạn đúng nhưng trong khi đó chỉ là bạn và anh ấy, bạn có thể muốn giữ một số điểm chung.


Cảm ơn vì sự sáng suốt. Bạn đạt điểm tốt. Tôi đồng ý rằng các trận chiến phải được chọn một cách khôn ngoan. Tôi đã thử điều "Này, kiểm tra email này", nhưng nó thường bị bỏ qua. Tôi là một người khắt khe về nó bởi vì loại mã "chỉ trong trường hợp" này đang trở nên phổ biến hơn trong cơ sở mã của chúng tôi. Nó làm cho mọi thứ trở nên không đặc biệt hơn, vì vậy tôi khó làm việc hơn. Có lẽ tôi nên thử và truyền đạt điểm chung đó thay vì tập trung vào một mẫu mã hóa cụ thể?
Jason Tyler

1

Vì câu hỏi kỹ thuật của bạn đã được trả lời (kiểm tra null là vô nghĩa), tôi muốn tập trung vào một khía cạnh khác của câu hỏi của bạn - làm thế nào để xử lý tình huống như vậy nói chung: một đồng nghiệp không biết một số sự kiện cơ bản về các công cụ mà tất cả các bạn sử dụng, và do đó sử dụng các công cụ theo cách không tối ưu.

Trong một số trường hợp, nó có thể không ảnh hưởng đến bạn nhiều. Nếu họ hoàn thành công việc và không can thiệp vào công việc của bạn, bạn có thể bỏ qua vấn đề.

Trong trường hợp khác (ví dụ như bạn), chúng ta can thiệp vào công việc của bạn, ít nhất là đến một mức độ nào. Phải làm gì về nó?

Tôi nghĩ rằng nó phụ thuộc vào rất nhiều chi tiết. Ví dụ, bạn và đồng nghiệp đã làm việc với công ty được bao lâu, công ty thường xử lý các vấn đề như thế nào, vấn đề này ảnh hưởng / làm phiền bạn như thế nào, bạn hòa hợp với đồng nghiệp như thế nào, quan trọng như thế nào là hài hòa mối quan hệ với đồng nghiệp của bạn với bạn, đến mức độ nào có thể đưa ra hoặc leo thang vấn đề ảnh hưởng đến mối quan hệ này, v.v.

Cá nhân tôi có thể là thế này: Tôi nghĩ các kỹ sư nên biết các công cụ của họ và các kỹ sư phần mềm nên biết ngôn ngữ lập trình của họ. Tất nhiên, không ai biết mọi chi tiết, nhưng các nhà xây dựng không bao giờ quay lại nulllà một ý tưởng rất cơ bản - mọi người nên biết điều đó. Khi tôi cần làm việc với một số mã có chứa các kiểm tra null vô nghĩa như vậy, tôi chỉ cần xóa chúng. Khi một đồng nghiệp hỏi tôi tại sao tôi loại bỏ chúng, tôi sẽ giải thích tại sao chúng là vô nghĩa. (Nếu cần, tôi cũng sẽ giải thích lý do tại sao mã hoàn toàn không cần thiết phải được xóa.) Về lâu dài, điều này sẽ dẫn đến mã không có các kiểm tra null vô nghĩa này.

Điều này có vẻ hơi kiêu ngạo, và nó có thể không phải là phương pháp phù hợp cho tất cả mọi người. Tôi đã nói rằng tôi kiên nhẫn và giỏi giải thích mọi thứ. Có lẽ đó là lý do tại sao tôi có xu hướng thoát khỏi cách tiếp cận này mà không gặp phải một kẻ ngốc. :-)

Bạn đề cập đến 'một yêu cầu chính thức rằng việc thực hành này phải được dừng lại'. Tôi chưa bao giờ làm việc trong một công ty có các quy tắc chính thức chi tiết như vậy, nhưng dù sao đây cũng là một vài suy nghĩ. Một lần nữa, tôi nghĩ rằng hành động tốt nhất của bạn phụ thuộc vào các chi tiết. Nếu gửi yêu cầu như vậy sẽ làm cho đồng nghiệp của bạn trông tệ trong mắt đồng nghiệp hoặc người quản lý, có lẽ tôi sẽ không làm điều đó. Mặt khác, nếu các yêu cầu như vậy thường được xem là một cải tiến đáng hoan nghênh và những người khác sẽ không đổ lỗi cho đồng nghiệp của bạn vì đã không tuân theo quy tắc này sớm hơn, hãy tiếp tục. Sẽ không ai bị hại, mọi người sẽ tốt hơn.


Nhưng nếu tôi không thể tìm ra cách tốt để giải quyết vấn đề, tôi có thể dùng đến sự mỉa mai và thêm mã như thế này ở mọi nơi:

int c = a + b;
if (c == a + b)
{
    // next steps
}
else 
{
    throw new ArithmeticException();
}

Nó chỉ là vô nghĩa như một kiểm tra null sau khi gọi hàm tạo, nhưng thậm chí còn rõ ràng hơn. Và tất nhiên, tôi sẽ không thực sự làm điều này. Trừ khi tôi rất khó chịu với đồng nghiệp của mình mà tôi dự định sẽ bỏ việc. ;-)

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.