Phân chia cách tính giá trị trả về và câu lệnh return trong phương thức một dòng?


26

Tôi đã có một cuộc thảo luận với đồng nghiệp về việc phá vỡ một returntuyên bố và tuyên bố tính toán giá trị trả về trong hai dòng.

Ví dụ

private string GetFormattedValue()
{
    var formattedString = format != null ? string.Format(format, value) : value.ToString();
    return formattedString;
}

thay vì

private string GetFormattedValue()
{
    return format != null ? string.Format(format, value) : value.ToString();
}

Thông thái mã Tôi không thực sự thấy một giá trị trong biến thể đầu tiên. Đối với tôi, cái sau rõ ràng hơn, đặc biệt là các phương pháp ngắn. Lập luận của ông cho dù biến thể cũ dễ gỡ lỗi hơn - đó là một công đức khá nhỏ, vì VisualStudio cho phép chúng tôi kiểm tra rất chi tiết các câu lệnh, khi việc thực thi bị dừng do điểm dừng.

Câu hỏi của tôi là, nếu nó vẫn là một điểm hợp lệ để viết mã ít rõ ràng hơn, chỉ để làm cho việc gỡ lỗi dễ dàng hơn? Có bất kỳ đối số nào nữa cho biến thể với phép tính và returncâu lệnh phân tách không?


18
Không hoạt động trên VS, nhưng sẽ cho rằng bạn không thể đặt điểm dừng có điều kiện trên biểu thức phức tạp (hoặc sẽ phức tạp để nhập), do đó có thể sẽ đặt gán và trả lại thành các câu lệnh riêng biệt, chỉ để thuận tiện. Trình biên dịch rất có thể sẽ đưa ra cùng một mã cho cả hai.
tofro

1
Điều này có thể phụ thuộc vào ngôn ngữ, đặc biệt là trong các ngôn ngữ nơi các biến có hành vi đối tượng (có thể phức tạp) thay vì hành vi con trỏ. Tuyên bố của @Paul K có lẽ đúng với các ngôn ngữ có hành vi con trỏ, ngôn ngữ nơi các đối tượng có hành vi giá trị đơn giản và ngôn ngữ có trình biên dịch chất lượng cao, trưởng thành.
MSalters

4
"Vì VisualStudio cho phép chúng tôi kiểm tra rất chi tiết các câu lệnh, khi việc thực thi bị dừng do điểm dừng" - là như vậy. Vậy làm thế nào để bạn nhận được giá trị trả về nếu hàm trả về một cấu trúc có nhiều hơn một thành viên? (và sự hỗ trợ cho tính năng đó là tốt nhất, có rất nhiều kết hợp mà bạn hoàn toàn không nhận được giá trị trả về).
Voo

2
Làm thế nào để có "kiểm tra rất chi tiết các câu lệnh" trong trình gỡ lỗi mà ai đó đang sử dụng HÔM NAY, làm cho nó trở thành một lựa chọn tồi để viết mã để dễ gỡ lỗi trong BẤT K deb trình gỡ lỗi nào?
Ian

16
Làm phiền anh ta hơn nữa bằng cách giảm toàn bộ cơ thể chức năng xuốngprivate string GetFormattedValue() => string.Format(format ?? "{0}", value);
Graham

Câu trả lời:


46

Giới thiệu các biến giải thích là một phép tái cấu trúc nổi tiếng đôi khi có thể giúp làm cho các biểu thức phức tạp dễ đọc hơn. Tuy nhiên, trong trường hợp hiển thị,

  • biến bổ sung không "giải thích" bất cứ điều gì không rõ ràng từ tên phương thức xung quanh
  • câu lệnh thậm chí còn dài hơn, vì vậy (hơi) ít đọc hơn

Hơn nữa, hầu hết các phiên bản mới hơn của trình gỡ lỗi Visual Studio có thể hiển thị giá trị trả về của hàm trong hầu hết các trường hợp mà không đưa ra biến thừa (nhưng hãy cẩn thận, có một số cảnh báo, hãy xem bài đăng SO cũ hơn này và các câu trả lời khác nhau ).

Vì vậy, trong trường hợp cụ thể này, tôi đồng ý với bạn, tuy nhiên, có những trường hợp khác mà một biến giải thích thực sự có thể cải thiện chất lượng mã.


Tôi cũng đồng ý rằng chắc chắn có những trường hợp hữu ích, không còn nghi ngờ gì nữa.
Paul Kertscher

2
Tôi thường sử dụng resultnhư tên của biến. Không lâu hơn và dễ dàng hơn để gỡ lỗi
edc65

26
@ edc65: một tên chung như result thường chỉ thêm tiếng ồn vào mã và hiếm khi làm tăng khả năng đọc, đó chính xác là điểm trả lời của tôi. Điều này có thể được biện minh trong ngữ cảnh nơi nó giúp gỡ lỗi, nhưng tôi sẽ tránh khi sử dụng trình gỡ lỗi không cần một biến riêng.
Doc Brown

6
@JonHanna cách công cụ dài theo tôi. Tên resulttruyền tải thông tin rằng đây là giá trị kết quả từ hàm, do đó bạn có thể xem xét nó trước khi hàm trả về.
edc65

1
@ edc65 nhưng điều đó làm cho nó hữu ích. Vì vậy, bây giờ khi tôi đọc mã của bạn, tôi không nhận ra ngay lập tức. Vì vậy, mã của bạn đã trở nên ít đọc hơn.
Jon Hanna

38

Đưa ra sự thật rằng:

a) Không có tác động đến mã cuối cùng vì trình biên dịch tối ưu hóa biến đi.

b) Có nó tăng cường khả năng gỡ lỗi.

Cá nhân tôi đã đi đến kết luận rằng đó là một cách thực hành tốt để tách chúng 99% thời gian.

Không có bất lợi vật chất để làm theo cách này. Đối số mà nó viết mã là một cách hiểu sai, bởi vì mã cồng kềnh là một vấn đề không đáng kể so với mã không thể đọc được hoặc khó gỡ lỗi. Hơn nữa, phương thức này không thể tự tạo mã khó hiểu, điều đó hoàn toàn phụ thuộc vào nhà phát triển.


9
Đây là câu trả lời chính xác cho tôi. Điều này giúp dễ dàng đặt điểm dừng và xem giá trị khi gỡ lỗi và không có nhược điểm nào tôi biết.
Matthew James Briggs

Đối với điểm b, trong Visual Studio Code, chỉ cần đặt điểm dừng trên lợi nhuận và sau đó thêm biểu thức: GetFormattedValue () và điều đó sẽ hiển thị kết quả khi điểm dừng được nhấn, do đó không cần thêm dòng. Nhưng sẽ dễ dàng hơn để xem các địa phương có thêm dòng vì nó sẽ không yêu cầu thêm bất kỳ biểu thức bổ sung nào trong trình gỡ lỗi. Vì vậy, thực sự là một vấn đề sở thích cá nhân.
Jon Raynor

3
@JonRaynor cho giá trị trả về, đặt điểm dừng trên khung đóng của hàm. Nó bắt giá trị trả về, ngay cả trong các hàm có nhiều trả về.
Baldrickk

16

Thông thường, việc giới thiệu một biến chỉ để đặt tên cho một số kết quả là rất hữu ích khi nó làm cho mã tự ghi lại nhiều hơn. Trong trường hợp này, đó không phải là một yếu tố vì tên biến rất giống với tên phương thức.

Lưu ý rằng các phương thức một dòng không có bất kỳ giá trị vốn có. Nếu một thay đổi giới thiệu nhiều dòng hơn nhưng làm cho mã rõ ràng hơn, đó là một thay đổi tốt.

Nhưng nói chung, những quyết định này phụ thuộc nhiều vào sở thích cá nhân của bạn. Ví dụ, tôi thấy cả hai giải pháp khó hiểu vì toán tử điều kiện đang được sử dụng không cần thiết. Tôi đã thích một câu lệnh if. Nhưng trong nhóm của bạn, bạn có thể đã đồng ý về các quy ước khác nhau. Sau đó, làm bất cứ điều gì quy ước của bạn đề nghị. Nếu các công ước im lặng trong một trường hợp như thế này, hãy lưu ý rằng đây là một thay đổi cực kỳ nhỏ không quan trọng trong thời gian dài. Nếu mô hình này xảy ra lặp đi lặp lại, có thể bắt đầu một cuộc thảo luận về cách bạn với tư cách là một nhóm muốn xử lý các trường hợp này. Nhưng đó là sự chia rẽ giữa những mã tốt và có lẽ là mã tốt hơn một chút.


1
"Tôi thấy cả hai giải pháp khó hiểu vì toán tử có điều kiện đang được sử dụng không cần thiết." - Đó không phải là một ví dụ thực tế, tôi chỉ cần nhanh chóng làm một cái gì đó. Phải thừa nhận rằng đây có thể không phải là ví dụ tốt nhất.
Paul Kertscher

4
+1 về cơ bản nói rằng đây là một sự khác biệt "dưới radar" (những thứ khác là bằng nhau) không đáng để bận tâm.
TripeHound

3
@Mindwin, khi tôi sử dụng toán tử ternary, tôi thường chia nó thành nhiều dòng để rõ ràng trường hợp thật và trường hợp sai là gì.
Arturo Torres Sánchez

2
@ ArturoTorresSánchez Tôi cũng làm vậy, nhưng thay vì một ?:tôi sử dụng if() {} else {- - - - \\ :)
Mindwin

3
@Mindwin, nhưng tôi không thể làm điều đó khi tôi ở giữa một biểu thức (như trình khởi tạo đối tượng)
Arturo Torres Sánchez

2

Để trả lời câu hỏi của bạn:

Câu hỏi của tôi là, nếu nó vẫn là một điểm hợp lệ để viết mã ít rõ ràng hơn, chỉ để làm cho việc gỡ lỗi dễ dàng hơn?

Vâng. Trong thực tế, một phần của tuyên bố trước đó của bạn dường như với tôi (không có vi phạm) để được một chút thiển cận (xem đậm bên dưới) " Lập luận của ông nào là các biến thể trước đây là dễ dàng hơn để gỡ lỗi - đó là khá một đức nhỏ , vì VisualStudio cho phép chúng tôi kiểm tra rất chi tiết các tuyên bố, khi việc thực thi bị dừng do điểm dừng. "

Làm cho việc gỡ lỗi dễ dàng hơn (hầu như) không bao giờ là " công đức nhỏ " bởi vì theo một số ước tính, 50% thời gian của lập trình viên được dành cho việc gỡ lỗi ( Phần mềm gỡ lỗi có thể đảo ngược ).

Có bất kỳ đối số nào nữa cho biến thể với phép tính tách và câu lệnh return không?

Vâng. Một số nhà phát triển sẽ lập luận rằng tính toán phân chia dễ đọc hơn. Điều này, tất nhiên, giúp gỡ lỗi nhưng cũng hỗ trợ khi ai đó đang cố giải mã bất kỳ quy tắc kinh doanh nào mà mã của bạn có thể thực hiện hoặc áp dụng.

LƯU Ý: Quy tắc kinh doanh có thể được phục vụ tốt hơn trong cơ sở dữ liệu vì chúng có thể thay đổi thường xuyên. Tuy nhiên, mã hóa rõ ràng trong lĩnh vực này vẫn là tối quan trọng. ( Cách xây dựng công cụ quy tắc kinh doanh )


1

Tôi sẽ đi xa hơn:

private string GetFormattedValue()
{
    if (format != null) {
        formattedString = string.Format(format, value);
    } else {
        formattedString = value.ToString()
    }
    return formattedString;
}

Tại sao?

Sử dụng các toán tử ternary cho logic phức tạp hơn sẽ không thể đọc được, vì vậy bạn sẽ sử dụng một kiểu như trên cho các câu lệnh phức tạp hơn. Bằng cách luôn luôn sử dụng phong cách này, mã của bạn phù hợp và dễ dàng hơn cho một người phân tích cú pháp. Ngoài ra, bằng cách giới thiệu loại thống nhất này (và sử dụng gợi ý mã và các thử nghiệm khác), bạn có thể tránh được goto faillỗi loại.

Một ưu điểm khác là báo cáo bảo hiểm mã của bạn sẽ cho bạn biết nếu bạn quên bao gồm một bài kiểm tra formatkhông phải là null. Đây không phải là trường hợp cho các nhà điều hành ternary.


Lựa chọn thay thế ưa thích của tôi - nếu bạn đang ở trong "nhận được lợi nhuận càng nhanh càng tốt" và không chống lại nhiều lợi nhuận từ một phương thức:

private string GetFormattedValue()
{
    if (format != null) {
        return string.Format(format, value);
    }

    return value.ToString();
}

Vì vậy, bạn có thể nhìn vào lần trả lại cuối cùng để xem mặc định là gì.

Điều quan trọng là phải nhất quán - và có tất cả các phương pháp của bạn tuân theo một hoặc quy ước khác.


1
Ví dụ đầu tiên có vẻ thực tiễn xấu vì value.ToString()được gọi là không cần thiết khi định dạng không phải là null. Trong trường hợp chung, điều này có thể bao gồm các phép tính không tầm thường và có thể mất nhiều thời gian hơn phiên bản bao gồm cả chuỗi định dạng. Ví dụ, hãy xem xét một valuePI lưu trữ một triệu vị trí thập phân và một chuỗi định dạng chỉ yêu cầu một vài chữ số đầu tiên.
Steve

1
tại sao không private string GetFormattedValue() => string.Format(format ?? "{0}", value); cùng ảnh hưởng và sử dụng các thử nghiệm đơn vị để đảm bảo tính chính xác thay vì dựa vào trình gỡ lỗi.
Berin Loritsch

1
Trong khi tôi đồng ý một ternary thể ít rõ ràng hơn, thì terminator null có thể làm cho mọi thứ rõ ràng hơn . Ít nhất là trong trường hợp này.
Berin Loritsch

1
Nhật ký thân mến, hôm nay tôi đã đọc rằng viết mã rõ ràng và súc tích bằng cách sử dụng các mô hình, thành ngữ và toán tử nổi tiếng (tồn tại khoảng 40 năm), trích dẫn, trích dẫn kép là trích dẫn kép thông minh, bỏ qua - nhưng thay vào đó, viết mã quá dài vi phạm DRY và không sử dụng các toán tử, thành ngữ và mô thức đã nói ở trên trong khi thay vào đó cố gắng tránh bất cứ thứ gì có thể có vẻ khó hiểu chỉ với một đứa trẻ năm tuổi không có nền tảng lập trình trước - thay vào đó là sự rõ ràng . Chết tiệt, tôi hẳn đã già thật rồi, nhật ký thân yêu của tôi ... Tôi nên học Go khi có cơ hội.
vaxquis

1
"Sử dụng các toán tử ternary cho logic phức tạp hơn sẽ không thể đọc được" Mặc dù thực tế là như vậy (và tôi đã thấy mọi người logic quá mức), điều này không đúng với mã của OP và cũng không phải là một điều cụ thể đối với các toán tử ternary. Điều duy nhất tôi có thể nói với sự tự tin hoàn toàn là dòng quá dài. gist.github.com/milleniumorms/cf9b62cac32a07899378feef6c06c776 là cách tôi sẽ định dạng lại nó.
thiên niên kỷ

1

Tôi không nghĩ rằng kỹ thuật như vậy có thể được chứng minh bằng nhu cầu gỡ lỗi. Tôi đã gặp phải cách tiếp cận này hàng ngàn lần và thỉnh thoảng tôi vẫn tiếp tục làm điều này, nhưng tôi luôn ghi nhớ những gì Martin Fowler nói về việc gỡ lỗi :

Mọi người cũng đánh giá thấp thời gian họ dùng để gỡ lỗi. Họ đánh giá thấp thời gian họ có thể dành để theo đuổi một con bọ dài. Với thử nghiệm, tôi biết ngay khi tôi thêm một lỗi. Điều đó cho phép tôi sửa lỗi ngay lập tức, trước khi nó có thể bò ra và ẩn đi. Có vài điều bực bội hoặc lãng phí thời gian hơn là gỡ lỗi. Sẽ không phải là một địa ngục nhanh hơn nhiều nếu chúng ta không tạo ra các lỗi ở nơi đầu tiên?


Martin Fowler là một người đàn ông thông minh và tôi rất thích đọc quan điểm của anh ấy (và của bạn). Mặc dù tôi tin chắc rằng việc kiểm tra là cần thiết và cần dành nhiều thời gian hơn cho nỗ lực đó, nhưng thực tế là tất cả chúng ta đều là con người dễ sai lầm cho thấy rằng không có số lượng thử nghiệm nào sẽ xóa được tất cả các lỗi. Do đó, gỡ lỗi sẽ luôn là một phần của quá trình hỗ trợ và phát triển chương trình.
tale852150

1

Tôi nghĩ rằng một số người đang bị treo lên về các vấn đề tiếp tuyến cho câu hỏi, chẳng hạn như các nhà điều hành ternary. Vâng, rất nhiều người ghét nó, vì vậy có lẽ nó vẫn tốt để đưa lên.

Liên quan đến trọng tâm của câu hỏi của bạn, chuyển câu lệnh được trả lại ra để được tham chiếu bởi một biến ...

Câu hỏi này đưa ra 2 giả định mà tôi không đồng ý:

  1. Biến thể thứ hai rõ ràng hơn hoặc dễ đọc hơn (tôi nói ngược lại là đúng) và

  2. mọi người đều sử dụng Visual Studio. Tôi đã sử dụng Visual Studio nhiều lần và có thể sử dụng nó tốt, nhưng tôi thường sử dụng một cái gì đó khác. Một môi trường dev buộc một IDE cụ thể là một môi trường mà tôi sẽ nghi ngờ.

Việc phá vỡ một cái gì đó thành một biến có tên hiếm khi làm cho mọi thứ khó đọc hơn, nó hầu như luôn làm điều ngược lại. Cách thức cụ thể mà một người nào đó làm điều đó có thể gây ra vấn đề, như nếu một lớp phủ tài liệu tự làm var thisVariableIsTheFormattedResultAndWillBeTheReturnValue = ...thì rõ ràng điều đó là xấu, nhưng đó là một vấn đề riêng biệt. var formattedText = ...Ổn.

Trong trường hợp cụ thể này và có thể nhiều trường hợp vì chúng ta đang nói về 1 lớp, biến sẽ không cho bạn biết nhiều rằng tên hàm chưa cho bạn biết. Do đó, biến không thêm nhiều. Đối số gỡ lỗi vẫn có thể giữ, nhưng một lần nữa, trong trường hợp cụ thể này, tôi không thấy bất cứ điều gì có thể là trọng tâm của bạn khi gỡ lỗi và nó luôn có thể dễ dàng thay đổi sau này nếu ai đó cần định dạng đó để gỡ lỗi hoặc bất cứ điều gì khác.

Nói chung, và bạn đã yêu cầu quy tắc chung (ví dụ của bạn chỉ là một ví dụ về hình thức tổng quát), tất cả các điểm được đưa ra có lợi cho biến thể 1 (2-liner) đều đúng. Đó là những hướng dẫn tốt để có. Nhưng hướng dẫn cần phải linh hoạt. Ví dụ: dự án tôi đang thực hiện hiện có tối đa 80 ký tự cho mỗi dòng, vì vậy tôi đã chia rất nhiều dòng, nhưng tôi thường tìm thấy các dòng 81-85 sẽ khó phân tách hoặc giảm khả năng đọc và tôi bỏ qua chúng giới hạn.

Vì không thể thêm giá trị, tôi sẽ không thực hiện 2 dòng cho ví dụ cụ thể được đưa ra. Tôi sẽ làm biến thể 2 (lớp 1) vì các điểm không đủ mạnh để làm khác trong trường hợp này.

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.