Tại sao nó được coi là một thực hành xấu để bỏ qua niềng răng xoăn? [đóng cửa]


177

Tại sao mọi người bảo tôi viết mã như thế này là một thực hành xấu?

if (foo)
    Bar();

//or

for(int i = 0 i < count; i++)
    Bar(i);

Lập luận lớn nhất của tôi về việc bỏ qua các dấu ngoặc nhọn là đôi khi nó có thể gấp đôi số dòng với chúng. Ví dụ, đây là một số mã để vẽ hiệu ứng phát sáng cho nhãn trong C #.

using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor)))
{
    for (int x = 0; x <= GlowAmount; x++)
    {
        for (int y = 0; y <= GlowAmount; y++)
        {
            g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y));
        }
     }
 }
 //versus
using (Brush br = new SolidBrush(Color.FromArgb(15, GlowColor)))
    for (int x = 0; x <= GlowAmount; x++)
        for (int y = 0; y <= GlowAmount; y++)
            g.DrawString(Text, this.Font, br, new Point(IconOffset + x, y));

Bạn cũng có thể nhận được thêm lợi ích của việc xâu chuỗi usingslại với nhau mà không phải thụt lề một triệu lần.

using (Graphics g = Graphics.FromImage(bmp))
{
    using (Brush brush = new SolidBrush(backgroundColor))
    {
        using (Pen pen = new Pen(Color.FromArgb(penColor)))
        {
            //do lots of work
        }
    }
 }
//versus
using (Graphics g = Graphics.FromImage(bmp))
using (Brush brush = new SolidBrush(backgroundColor))
using (Pen pen = new Pen(Color.FromArgb(penColor)))
{
    //do lots of work
}

Đối số phổ biến nhất cho dấu ngoặc nhọn xoay quanh lập trình bảo trì và các vấn đề sẽ xảy ra bằng cách chèn mã giữa câu lệnh if gốc và kết quả dự định của nó:

if (foo)
    Bar();
    Biz();

Câu hỏi:

  1. Có sai không khi muốn sử dụng cú pháp nhỏ gọn hơn mà ngôn ngữ cung cấp? Những người thiết kế các ngôn ngữ này rất thông minh, tôi không thể tưởng tượng được họ sẽ sử dụng một tính năng luôn xấu khi sử dụng.
  2. Chúng ta nên hay không nên viết mã để mẫu số chung thấp nhất có thể hiểu và không có vấn đề gì khi làm việc với nó?
  3. Có một lập luận khác mà tôi đang thiếu?

6
Tôi đồng ý với bạn. Bỏ qua chúng. Giai đoạn = Stage.
Andrei Rînea

67
AI CHĂM SÓC VỀ NHIỀU DÒNG ÁO DÀI LÀ NÓ TRONG NĂM 2010. Màn hình rộng và rẻ và độ phân giải cao! Màn hình của tôi là 2048 X 1152 và tôi có HAI cái đó! Khả năng đọc là cách quan trọng hơn khi lưu 2 dòng dọc khi bạn có thể dễ dàng đưa ra các lỗi tinh vi khó tìm.

51
Màn hình rộng và rẻ, nhưng chúng không cao và rẻ. Không gian dọc khan hiếm hơn không gian ngang.
Adam nói - Phục hồi lại

32
@AdamRuth Xoay chúng sang một bên :)
Zachary Yates

16
Vì vậy, bạn không gặp rắc rối như Apple với lỗi SSL được tìm thấy vào tháng 2 năm 2014, LOL.
tìm hiểu

Câu trả lời:


183

Trên thực tế, lần duy nhất mà tôi thực sự cắn tôi là khi tôi gỡ lỗi và nhận xét thanh ():

if(foo)
  // bar();
doSomethingElse();

Ngoài ra, tôi có xu hướng sử dụng:

if(foo) bar();

Mà chăm sóc các trường hợp trên.

EDIT Cảm ơn bạn đã làm rõ câu hỏi, tôi đồng ý, chúng ta không nên viết mã cho mẫu số chung thấp nhất.


31
Tất nhiên, điều đó thật khó khăn, nhưng tôi nghĩ vấn đề lớn nhất đối với các nhà bảo trì là họ sẽ thêm một dòng thứ hai và không nhận ra đó không phải là một phần của tuyên bố có điều kiện mặc dù nó thực sự có vẻ như vậy.
danieltalsky

23
Tôi không thích kiểu áo lót vì tôi luôn tìm kiếm thân vòng bên dưới.
Nosredna

18
Tôi thấy kiểu một lớp lót khó chịu hơn là hữu ích bởi vì (đặc biệt là trong việc lồng sâu - ugh), câu lệnh có thể dễ dàng bị đọc quá mức và sự nhầm lẫn có thể xảy ra. Tôi hầu như chỉ sử dụng các câu lệnh đơn như vậy để xác thực đầu vào (tức là trả về sớm) hoặc điều khiển vòng lặp (ví dụ bỏ qua các tên tệp không liên quan trong các bước của hệ thống tệp), tuy nhiên, trong đó các dòng trống giúp tắt chúng khỏi mã chính.
Alan Plum

1
Kiểu một dòng cũng ẩn thanh () khỏi phạm vi kiểm tra. Nó sẽ được bảo hiểm ngay cả khi foo luôn luôn sai.
Bohumil Janda

1
Đây là một cách khác để nói, "có nguồn bao gồm các dấu ngoặc nhọn, tôi sẽ không lãng phí thời gian trong khi gỡ lỗi." - thêm niềng răng là đơn giản tầm thường, và tránh để lại cạm bẫy cho người tiếp theo. Đối số duy nhất chống lại là "khả năng đọc", khá khó hiểu, cho rằng đây là trường hợp xảy ra điều gì đó ngầm nếu bạn bỏ qua chúng.
Andrew Theken

156

Tốc độ đọc ...

Ngoài những gì đã được đề cập. Tại thời điểm này, tôi đã có điều kiện phân tích cú pháp nếu các câu lệnh có dấu ngoặc nhọn và khoảng trắng. Vì vậy, tôi đọc:

if (condition)
{
    DoSomething();
}

DoSomethingElse();

Hơi nhanh hơn tôi đọc:

if (condition) DoSomething();

DoSomethingElse();

Tôi đọc nó chậm hơn một chút nếu nó trông như thế này:

if (condition) DoSomething();
DoSomethingElse();

Tôi đọc điều này chậm hơn đáng kể so với trước:

if (condition) 
    DoSomething();
DoSomethingElse();

beause Tôi không thể không đọc nó chỉ trong trường hợp và tự hỏi nếu tác giả có ý định:

if (condition)
{
    DoSomething();
    DoSomethingElse();
}

Đã được nói chung, nhưng khi đọc phần bên dưới, tôi sẽ xem xét điều này khá lâu để đảm bảo những gì tác giả dự định. Tôi thậm chí có thể săn lùng tác giả gốc để xác nhận.

if (condition) 
    DoSomething();
    DoSomethingElse();

29
Vấn đề được giải quyết khi, trong mẫu thứ 4 của bạn, bạn chỉ cần đặt một dòng trống sau câu lệnh if để tách nó khỏi DoS SomethingElse () Đó là những gì tôi làm và khả năng đọc thực tế giống như với mẫu thứ nhất (nếu không tốt hơn ;-P) Một điều nữa là việc đặt các dòng trong nhóm 2 hoặc 3 một cách rõ ràng giúp dễ đọc, bạn chỉ cần quét qua mã nhanh hơn nhiều.
Piotr Owsiak

6
Có thể đó là vì tôi đã quen với Python, nhưng đặt dấu ngoặc nhọn đầu tiên trên cùng dòng với câu lệnh if cho phép tôi đọc + hiểu nó nhanh hơn.
Ponkadoodle

16
Vấn đề với phong cách không tàn nhẫn, nó khiến bạn lãng phí sự tập trung quý báu của mình khi nghĩ liệu khối đó có niềng răng hay không hơn là những thứ quan trọng hơn. Điều này một mình là một lý do đủ tốt để sử dụng quy ước đơn giản nhất có thể, đó là niềng răng luôn.
Nate CK

2
Niềng răng được bản chất rõ ràng. Tôi sẽ gắn bó với điều đó, cảm ơn.
TheOptimusPrimus

2
Trong trường hợp cuối cùng, săn lùng tác giả gốc và đánh đòn anh ta ...;)
Per Lundberg

55

Nếu đó là một cái gì đó nhỏ, viết nó như thế này:

if(foo()) bar();

Nếu nó đủ dài để chia thành hai dòng, sử dụng niềng răng.


Vâng, đó là những gì tôi làm quá. Nó buộc bạn phải thêm dấu ngoặc nhọn nếu bạn thêm một dòng khác và thật rõ ràng khi thấy thanh đó () thực sự là một phần của if, vì vậy những điều tồi tệ sẽ xảy ra nếu bạn chỉ nhận xét.
jalf

10
Mặc dù không thân thiện với trình gỡ lỗi. Làm thế nào để bạn thiết lập một điểm dừng trên phần "thanh"?
Nemanja Trifunovic

9
@Nemanja: Chỉ cần đặt con trỏ của bạn trên thanh (); và nhấn F9. Cả VS2005 và VS2008 đều xử lý các điểm dừng nội tuyến, nếu chúng là các "câu lệnh" riêng biệt.
GalacticCowboy

4
GalanticCowboy: Có nhiều môi trường hơn những môi trường mà bạn biết. :-)

20
Chắc chắn, nhưng vì bối cảnh là C #, nên sẽ chiếm phần lớn đáng kể người dùng ...
GalacticCowboy

47

Tôi cũng từng nghĩ sẽ tốt hơn nếu chỉ sử dụng niềng răng khi thực sự cần thiết. Nhưng không còn nữa, lý do chính, khi bạn có nhiều mã, nó làm cho nó dễ đọc hơn và bạn có thể phân tích mã nhanh hơn khi bạn có một kiểu giằng phù hợp.

Một lý do chính đáng khác để luôn luôn sử dụng niềng răng, bên cạnh việc ai đó thêm câu lệnh thứ hai vào if, là một điều như thế này có thể xảy ra:

if(a)
   if(b)
     c();
else
   d();

Bạn có để ý rằng mệnh đề khác thực sự là mệnh đề "if (b)" không? Bạn có thể đã làm, nhưng bạn sẽ tin tưởng bất cứ ai quen thuộc với gotcha này?

Vì vậy, nếu chỉ vì sự nhất quán và bởi vì bạn không bao giờ biết điều gì bất ngờ có thể xảy ra khi người khác (luôn luôn là người khác ngu ngốc) thay đổi mã, tôi luôn đặt dấu ngoặc, vì nó làm cho mã nguồn dễ đọc hơn, nhanh hơn để phân tích bộ não của bạn. Chỉ đối với các câu lệnh if đơn giản nhất, như nếu một ủy nhiệm được thực hiện hoặc giống như chuyển đổi, nơi bạn biết mệnh đề sẽ không bao giờ được gia hạn, tôi sẽ bỏ qua các dấu ngoặc nhọn.


12
Đây là một trong hai trường hợp tôi sử dụng niềng răng. Nếu không thì không.
Andrei Rînea

3
Điều này nên được viết như thể (a && b) ... ở vị trí đầu tiên.
alexander.biskop

8
@ alexander.biskop: Chắc chắn là không, vì điều đó mang lại cho người khác một ý nghĩa khác ( !a || !b) hơn là với ( !a) hoặc không có ( a && !b) thêm dấu ngoặc nhọn.
Ben Voigt

1
@Ben Voigt: Đúng! Đã dành cho nó ;-) Nửa năm và hai lần nâng cấp sau đó, ai đó nhận ra tuyên bố tôi đưa ra là sai ... Đoán tôi không chú ý đến chi tiết, chủ yếu là vì ý định đằng sau nhận xét của tôi là khác với chỉ ra một chuyển đổi chính xác về mặt kỹ thuật của mệnh đề if đó. Điều tôi muốn nói là các câu lệnh if nội tuyến lồng nhau thường làm giảm khả năng đọc (và đặc biệt là khả năng truy nguyên nguồn gốc của luồng kiểm soát) và do đó nên được kết hợp thành một câu lệnh (hoặc tránh hoàn toàn). Chúc mừng
alexander.biskop

3
@ alexander.biskop: Đồng thời, việc gỡ lỗi dễ dàng hơn với các if lồng nhau với các điều kiện đơn giản hơn, vì bạn có thể bước từng bước một.
Ben Voigt

35

Đây không phải luôn luôn được coi là một thực hành xấu. Các nguyên tắc mã hóa dự án Mono đề nghị không sử dụng dấu ngoặc nhọn nếu nó không cần thiết. Điều tương tự đối với các tiêu chuẩn mã hóa GNU . Tôi nghĩ đó là vấn đề sở thích cá nhân như mọi khi với các tiêu chuẩn mã hóa.


2
Nhìn vào mã Mono có vẻ khá xa lạ với tôi sau khi đọc toàn bộ hướng dẫn.
dance2die

35

Hàng có giá rẻ. Công suất xử lý rẻ. Thời gian phát triển rất tốn kém.

Theo nguyên tắc chung, trừ khi tôi đang phát triển một số ứng dụng quan trọng hoàn toàn về tài nguyên / tốc độ, tôi sẽ luôn luôn sai về phía viết mã đó là

(a) Dễ dàng cho bất kỳ nhà phát triển nào khác làm theo những gì tôi đang làm

(b) Nhận xét các phần cụ thể của mã có thể cần nó

(c) Dễ dàng gỡ lỗi nếu có sự cố

(d) Dễ dàng sửa đổi nếu cần trong tương lai (nghĩa là thêm / xóa mã)

Tốc độ hoặc sự tao nhã trong học thuật của mã là thứ yếu đối với các yếu tố này từ góc độ Kinh doanh. Điều này không có nghĩa là tôi bắt đầu viết mã lố lăng hoặc xấu xí, nhưng đây là thứ tự ưu tiên của tôi.

Bằng cách bỏ qua các dấu ngoặc nhọn trong hầu hết các trường hợp, điều đó đối với tôi làm cho (b), (c) và (d) trở nên khó khăn hơn (tuy nhiên lưu ý không phải là không thể). Tôi sẽ nói rằng sử dụng niềng răng xoăn hoặc không ảnh hưởng đến (a).


9
Tuyên bố cuối cùng của bạn về (a) là một ý kiến ​​rằng một số lượng lớn các nhà phát triển, bao gồm các áp phích khác ở đây, sẽ tranh luận.
Kelly S. Pháp

34

Tôi thích sự rõ ràng mà niềng răng cung cấp. Bạn biết chính xác những gì có nghĩa và không phải đoán nếu ai đó vừa bị lừa và bỏ chúng đi (và đưa ra một lỗi). Lần duy nhất tôi bỏ qua chúng là khi tôi đặt if và hành động trên cùng một dòng. Tôi cũng không làm điều đó thường xuyên. Tôi thực sự thích khoảng trắng được giới thiệu bằng cách đặt dấu ngoặc nhọn trên dòng riêng của nó, mặc dù từ nhiều năm lập trình giống như K & R C, kết thúc dòng bằng một dấu ngoặc là một cách tôi phải làm để khắc phục nếu IDE không thi hành nó tôi.

if (condition) action();  // ok by me

if (condition) // normal/standard for me
{
   action();
}

23

Tôi nghĩ đó là vấn đề hướng dẫn cho dự án bạn đang thực hiện và sở thích cá nhân.

Tôi thường bỏ qua chúng khi không cần thiết, ngoại trừ một số trường hợp như sau:

if (something)
    just one statement; // i find this ugly
else
{
    // many
    // lines
    // of code
}

tôi thích

if (something)
{
    just one statement; // looks better:)
}
else
{
    // many
    // lines
    // of code
}

15

Một trong những trường hợp điều này có thể cắn bạn đã trở lại trong thời đại cũ của macro C / C ++. Tôi biết đây là câu hỏi C #, nhưng thường các tiêu chuẩn mã hóa được thực hiện mà không có lý do tại sao tiêu chuẩn được tạo ra ngay từ đầu.

Nếu bạn không cẩn thận khi tạo macro, cuối cùng bạn có thể gây ra sự cố với nếu các câu lệnh không sử dụng {}.

#define BADLY_MADE_MACRO(x) function1(x); function2(x);

if (myCondition) BADLY_MADE_MACRO(myValue)

Bây giờ, đừng hiểu sai ý tôi, tôi không nói rằng bạn nên luôn luôn làm {} chỉ để tránh vấn đề này trong C / C ++, nhưng tôi đã phải đối phó với một số lỗi rất lạ vì điều này.


2
nếu chỉ có tất cả các mã tự tuyên bố như vậy ... thở dài
Nathan Strong

15

Tôi sử dụng để suy nghĩ theo cùng một cách.

Cho đến một ngày (tại sao luôn luôn có "một ngày" thay đổi cuộc sống của bạn mãi mãi?) Chúng tôi dành từ 24 - 36 giờ liên tục mà không ngủ gỡ lỗi mã sản xuất chỉ để tìm ra ai đó đã không niềng răng kết hợp với thay đổi tìm kiếm / thay thế .

Nó là một cái gì đó như thế này.

 if( debugEnabled ) 
      println( "About to save 1 day of work to some very important place.");
 saveDayData();

Những gì đến sau là

 if( debugEnabled ) 
 //     println( "About to save 1 day of work to some very important place.");
 saveDayData();

Hóa ra hệ thống đã tạo ra 500 mb nhật ký hàng ngày và chúng tôi được yêu cầu dừng nó. Cờ gỡ lỗi là không đủ để tìm kiếm và thay thế println theo thứ tự.

Tuy nhiên, khi ứng dụng đi vào sản xuất, cờ gỡ lỗi đã tắt và "saveDayData" quan trọng không bao giờ được gọi.

BIÊN TẬP

Bây giờ, nơi duy nhất tôi không sử dụng niềng răng là trong / thử xây dựng.

if( object != null ) try { 
     object.close();
} catch( .....

Sau khi xem một nhà phát triển siêu sao làm điều đó.


3
Tôi không hiểu Bạn có một biến điều khiển gỡ lỗi (debugEnables) và bạn vẫn sử dụng các bình luận để vô hiệu hóa gỡ lỗi ??? Tại sao bạn không đặt debugEnables thành false?
Igor Popov

Đây không phải là chính xác kịch bản, nhưng bạn làm cho một điểm tốt. Những điều ngu ngốc (như không đặt gỡ lỗi thành sai) sẽ tạo ra các lỗi tinh vi và ngu ngốc khó tìm hơn các lỗi "rõ ràng". Không sử dụng niềng răng đã không giúp quá nhiều trong trường hợp này.
OscarRyz

1
@igor Làm thế nào vì họ chỉ muốn xóa một dòng đăng nhập không phải tất cả các dòng đăng nhập?
gman

14

Nói thẳng ra tôi thấy nó như sau:

Lập trình viên giỏi lập trình phòng thủ, lập trình viên xấu thì không.

Vì có một vài ví dụ ở trên và những trải nghiệm tương tự của riêng tôi với các lỗi liên quan đến việc quên niềng răng, sau đó tôi đã học được cách khó khăn để LUÔN LUÔN.

Bất cứ điều gì khác là chọn phong cách cá nhân trên sự an toàn và đó rõ ràng là lập trình xấu.

Joel thậm chí còn đề cập đến điều này trong việc tạo mã sai

Một khi bạn gặp phải một lỗi do mất niềng răng, bạn biết rằng việc niềng răng bị thiếu có vẻ sai vì bạn biết đó là nơi tiềm năng cho một lỗi khác xảy ra.


Nếu niềng răng mở được đặt trên các dòng một mình, thì bất cứ nơi nào mà hai hoặc nhiều dòng được thụt vào mà không có cặp niềng sẽ nhìn sai, cũng như bất kỳ cặp niềng răng nào. Việc sử dụng như vậy sẽ tiêu tốn một dòng trống trong trường hợp ifcâu lệnh điều khiển một khối, nhưng tránh sự cần thiết của khối trong trường hợp ifcâu lệnh điều khiển một hành động đơn lẻ thay vì một khối.
supercat

14

Tôi khá vui khi:

foreach (Foo f in foos)
  foreach (Bar b in bars)
    if (f.Equals(b))
      return true;

return false;

Cá nhân, tôi không thấy lý do tại sao

foreach (Foo f in foos)
{
  foreach (Bar b in bars)
  {
    if (f.Equals(b))
    {
      return true;
    }
  }
}

return false;

là bất kỳ dễ đọc hơn.

Có, các dòng là miễn phí, nhưng tại sao tôi phải cuộn qua các trang và trang mã khi nó có thể bằng một nửa kích thước?

Nếu có sự khác biệt về khả năng đọc hoặc khả năng bảo trì thì chắc chắn, hãy đặt niềng răng vào ... nhưng trong trường hợp này tôi không thấy bất kỳ lý do nào.

Ngoài ra, tôi sẽ luôn đặt niềng răng cho lồng nếu đó là nơi tôi đã lồng

if (condition1)
  if (condition2)
    doSomething();
  else (condition2)
    doSomethingElse();

đấu với

if (condition1)
  if (condition2)
    doSomething();
else (condition2)
  doSomethingElse();

là rất khó hiểu, vì vậy tôi luôn viết nó như sau:

if (condition1)
{
  if (condition2)
    doSomething();
  else (condition2)
    doSomethingElse();
}

Bất cứ khi nào có thể, tôi sử dụng các toán tử ternary, nhưng tôi không bao giờ lồng chúng .


chỉ thấy điều này gần như xảy ra trong mã của tôi :) hình dung tôi sẽ có một cái nhìn xung quanh, và lo lắng ... nó đã ở ngoài đó :)
Noctis

10

Tôi đồng ý rằng "nếu bạn đủ thông minh để khiến ai đó trả tiền cho bạn để viết mã, bạn nên đủ thông minh để không chỉ dựa vào thụt lề để xem dòng mã."

Tuy nhiên ... có thể mắc lỗi và đây là một lỗi để gỡ lỗi ... đặc biệt nếu bạn đang xem mã của người khác.


10

Triết lý của tôi là nếu nó làm cho mã dễ đọc hơn, tại sao không làm điều đó?

Rõ ràng bạn phải vẽ đường thẳng ở đâu đó, giống như tìm phương tiện hạnh phúc đó giữa các tên biến mô tả ngắn gọn và quá mô tả. Nhưng dấu ngoặc thực sự tránh được lỗi và cải thiện khả năng đọc của mã.

Bạn có thể lập luận rằng những người đủ thông minh để trở thành lập trình viên sẽ trở nên đủ thông minh để tránh các lỗi phát sinh từ các câu lệnh không dấu ngoặc. Nhưng bạn có thể thành thật nói rằng bạn chưa bao giờ bị vấp phải một lỗi đơn giản như lỗi chính tả không? Minutia như thế này có thể áp đảo khi nhìn vào các dự án lớn.


7
Tất nhiên điều này rất chủ quan. Để chúng ra đôi khi cải thiện khả năng đọc.
Brian Knoblauch

Đúng, nếu tôi bỏ dấu ngoặc ra, tôi giữ một dòng, như những người khác đã đề cập. Tôi đã bị cắn quá nhiều lần nhưng những câu không có khung nhiều dòng. Ngay cả khi bạn biết để ý đến nó, nó vẫn có thể thỉnh thoảng có được bạn.
James McMahon

10

Luôn có những trường hợp ngoại lệ, nhưng tôi sẽ tranh luận về việc bỏ niềng răng chỉ khi nó ở một trong các hình thức:

if(x == y)
   for(/* loop */)
   {
      //200 lines
   }

//rampion's example:
for(/* loop */)
{
   for(/* loop */)
      for(/* loop */)
      {
         //several lines
      }
}

Nếu không, tôi không có vấn đề với nó.


4
Ví dụ xấu. Trong cả hai trường hợp đó, tôi sẽ tính 200 dòng đó cho vòng lặp vào phương thức riêng của nó, hoặc tốt nhất là một số phương thức.
Adam Jaskiewicz

2
Đúng, nhưng nó xảy ra. Ngoài ra, bất cứ khi nào một khối dài hơn màn hình của trình đọc cao, bạn sẽ gặp vấn đề này. Và bạn không thể kiểm soát chiều cao màn hình của trình đọc mã của mình. Họ có thể chỉ nhìn thấy một vài dòng ở mỗi bên.
hung hăng

2
Nó xảy ra. Điều đó không có nghĩa là nó sẽ xảy ra.
Adam Jaskiewicz

3
@AdamJaskiewicz Phải. Nó xảy ra. Chúng ta phải suy nghĩ về những gì không xảy ra, chứ không phải là những gì nên xảy ra. Nếu chúng ta có thể hạn chế những gì sẽ xảy ra, chúng ta sẽ không cần phải lo lắng về lỗi.
Beska

10

Thỉnh thoảng tôi sử dụng mã dưới cùng (nhiều câu lệnh sử dụng), nhưng khác với việc tôi luôn đặt dấu ngoặc nhọn. Tôi chỉ thấy nó làm cho mã rõ ràng hơn. Rõ ràng là rõ ràng hơn là chỉ thụt lề rằng một tuyên bố là một phần của một khối (và do đó có lẽ là một phần của if vv).

Tôi đã thấy

if (...)
    foo();
    bar();

lỗi cắn tôi (hay đúng hơn là "tôi và đồng nghiệp" - tôi đã không thực sự giới thiệu lỗi này) một lần . Điều này bất chấp thực tế là các tiêu chuẩn mã hóa của chúng tôi tại thời điểm đó được khuyến nghị sử dụng niềng răng ở mọi nơi. Phải mất một thời gian dài đáng ngạc nhiên để phát hiện ra - bởi vì bạn thấy những gì bạn muốn thấy. (Đây là khoảng 10 năm trước. Có lẽ tôi sẽ thấy nó nhanh hơn bây giờ.)

Tất nhiên, nếu bạn sử dụng "nẹp ở cuối dòng", nó sẽ giảm các dòng phát sinh thêm, nhưng cá nhân tôi không thích phong cách đó. (Tôi sử dụng nó tại nơi làm việc và thấy nó ít khó chịu hơn tôi mong đợi, nhưng nó vẫn hơi khó chịu.)


Tôi luôn tranh luận trường hợp rằng tính nhất quán quan trọng hơn phong cách thực tế, tại sao chúng ta chỉ nên tạo một ngoại lệ trong trường hợp sử dụng?
Bob

@Bob: Điểm tốt, và tôi chỉ thỉnh thoảng làm điều đó. Tôi không muốn giả vờ rằng tôi có lý do thực sự ở đây :) Trên thực tế, có một lý do hợp lý - lồng các cách sử dụng (và chỉ sử dụng) như thế là khá rõ ràng, và bạn vẫn kết thúc với một khối. Tôi không thể thấy ngay sự nguy hiểm.
Jon Skeet

Tất nhiên, điều đó không có nghĩa là không có bất kỳ nguy hiểm nào.
Jon Skeet

1
Giải pháp tốt nhất cho lỗi này: Sử dụng Python. (Đùa, tất nhiên.)
Joel Wietelmann

10

Tôi rất ấn tượng và khiêm tốn rằng các đồng nghiệp của tôi trong lĩnh vực lập trình máy tính này (bạn rất nhiều) không bị nản chí bởi triển vọng của các lỗi tiềm ẩn khi bạn bỏ qua các dấu ngoặc trên các khối đơn.

Tôi cho rằng nó có nghĩa là tôi không thông minh. Tôi đã phạm sai lầm xung quanh điều này nhiều lần. Tôi đã gỡ lỗi những sai lầm của người khác xung quanh điều này. Tôi đã xem phần mềm bị lỗi vì điều này (RDP cho một máy chạy VS2002 và phông chữ cửa sổ đồng hồ của bạn sẽ trở nên khó khăn).

Nếu tôi nhìn vào tất cả những sai lầm mà tôi đã mắc phải có thể tránh được với sự thay đổi trong phong cách mã hóa, thì danh sách này rất dài. Nếu tôi không thay đổi cách tiếp cận của mình trong từng trường hợp này, có lẽ tôi sẽ không bao giờ thực hiện nó như một lập trình viên. Một lần nữa, tôi đoán tôi không thông minh. Để bù đắp, tôi đã là một người sử dụng trung thành của niềng răng trên các khối đơn trong một thời gian dài.

Điều đó nói rằng, một số điều đã thay đổi trên thế giới khiến cho quy tắc "ngươi sẽ sử dụng niềng răng trên các khối đơn tuyến" ngày nay ít liên quan hơn so với khi Moses đưa nó xuống cho chúng ta:

  • Một số ngôn ngữ phổ biến làm cho vấn đề biến mất bằng cách làm cho máy tính đọc thụt lề, giống như lập trình viên làm (ví dụ Python).

  • Trình chỉnh sửa của tôi tự động định dạng cho tôi, vì vậy khả năng tôi bị đánh lạc hướng bằng cách thụt lề giảm đi nhiều.

  • TDD có nghĩa là nếu tôi giới thiệu một lỗi vì tôi bị nhầm lẫn bởi một khối đơn dòng, tôi có nhiều khả năng phát hiện ra lỗi nhanh chóng.

  • Tái cấu trúc và biểu cảm ngôn ngữ có nghĩa là các khối của tôi ngắn hơn nhiều và các khối đơn dòng xảy ra thường xuyên hơn nhiều so với trước đây. Theo giả thuyết, với một ứng dụng ExtractMethod tàn nhẫn, tôi có thể chỉ có các khối đơn dòng trong toàn bộ chương trình của mình. (Tôi tự hỏi nó sẽ trông như thế nào?)

Trên thực tế, có một lợi ích khác biệt có thể đến từ việc tái cấu trúc một cách tàn nhẫn & bỏ sót niềng răng trên các khối đơn: khi bạn thấy niềng răng, một chút báo động có thể tắt trong đầu bạn rằng "sự phức tạp ở đây! Hãy coi chừng!". Hãy tưởng tượng nếu đây là tiêu chuẩn:

if (condition) Foo();   // normal, everyday code

if (condition) 
{
    // something non-trivial hapening; pay attention!
    Foo();
    Bar();
}

Tôi đang mở ra ý tưởng thay đổi quy ước mã hóa của mình thành một cái gì đó như "các khối đơn dòng có thể không bao giờ có dấu ngoặc" hoặc "nếu bạn có thể đặt khối trên cùng một dòng với điều kiện và tất cả chỉ nằm trong 80 ký tự, bỏ qua niềng răng ". Chúng ta sẽ thấy.


Là một lập trình viên Java trước đây, tôi phải hoàn toàn đồng ý với định dạng tự động là câu trả lời thực sự cho vấn đề. Bạn thậm chí không cần phải có trình soạn thảo của mình thêm tự động niềng răng - tự động thụt lề có thể giúp tránh sự mơ hồ rất nhiều. Tất nhiên bây giờ tôi đã chuyển sang Python, tôi đã quen với việc định dạng mã của mình ngay từ đầu.
Alan Plum

Tôi cảm thấy như vậy về niềng răng. Đó là tất cả về sự phức tạp. Lý tưởng nhất là chúng ta sẽ chỉ có một khối dòng. Đó là những gì tôi gọi là dễ đọc, không phải niềng răng không cần thiết.
Igor Popov

9

Trong ba công ước:

if(addCurleyBraces()) bugFreeSofware.hooray();

và:

if(addCurleyBraces())
    bugFreeSofware.hooray();

và (đại diện cho bất kỳ kiểu thụt đầu dòng nào bằng cách sử dụng một dấu ngoặc mở và đóng):

if(addCurleyBraces()) {
    bugFreeSofware.hooray();
}

Tôi thích cái cuối cùng là:

  • Tôi thấy dễ đọc hơn nếu tất cả các câu lệnh if được viết một cách thống nhất.
  • Nó có thể làm cho phần mềm mạnh mẽ hơn một chút và không có lỗi. Tuy nhiên, tất cả các trình soạn thảo văn bản nâng cao và hiện đại của IDE đều có các tính năng tự động thụt lề đẹp mà tôi nghĩ mọi người nên sử dụng miễn là nó không làm rối định dạng nhận xét hoặc đi ngược lại các tiêu chuẩn nhóm (trong nhiều trường hợp có thể tạo sơ đồ định dạng tùy chỉnh và chia sẻ nó với nhóm). Quan điểm của tôi ở đây là nếu thụt lề được thực hiện chính xác thì nguy cơ giới thiệu lỗi sẽ giảm đi một chút.
  • Tôi thích biểu thức và câu lệnh boolean để thực thi trên các dòng khác nhau. Tôi muốn có thể đánh dấu một hàng cho mục đích gỡ lỗi. Ngay cả khi tôi đang sử dụng IDE nơi tôi có thể đánh dấu một câu lệnh và bước tới nó, đó là một hoạt động tương tác và tôi có thể quên nơi tôi bắt đầu gỡ lỗi hoặc ít nhất nó sẽ khiến tôi mất thêm một chút thời gian để bước qua mã lần (vì tôi phải đánh dấu vị trí thủ công mỗi lần trong khi gỡ lỗi).

8

Đối số chính của bạn chống lại việc sử dụng dấu ngoặc nhọn là chúng sử dụng các dòng bổ sung và chúng yêu cầu thụt lề bổ sung.

Các dòng là (gần như) miễn phí, giảm thiểu số lượng dòng trong mã của bạn không phải là một mục tiêu.

Và thụt độc lập với việc sử dụng nẹp. Trong ví dụ 'sử dụng' tầng của bạn, tôi vẫn nghĩ rằng bạn nên thụt lề chúng ngay cả khi bạn bỏ qua dấu ngoặc nhọn.


8

Tôi là một người tin tưởng mạnh mẽ vào việc viết mã gọn gàng và súc tích, nhưng tôi sẽ luôn sử dụng các dấu ngoặc nhọn. Tôi thấy rằng chúng là một cách thuận tiện để nhanh chóng nhìn thấy phạm vi tồn tại một dòng mã cụ thể. Không có sự mơ hồ, nó chỉ được đặt ra rõ ràng trước mặt bạn.

Một số người có thể nói đó là một trường hợp ưu tiên, nhưng tôi thấy luồng logic của chương trình dễ theo dõi hơn nếu nó nhất quán trong nội bộ và tôi không tin rằng việc viết một câu lệnh IF như thế này là nhất quán;

if(x < y)
    x = y;
else
    y = x;

Và một cái khác như thế này;

if(x < y)
{
    x = y;
    x++;
}
else
{
    y = x;
    y++;
}

Tôi thích chỉ chọn một phong cách chung và gắn bó với nó :)


7

Một trong những vấn đề chính là khi bạn có các vùng của một lớp lót và không phải một lớp lót, cùng với việc tách khỏi quy định kiểm soát ( for,, ifbạn có gì) và kết thúc quy chế.

Ví dụ:

for (...)
{
  for (...)
    for (...) 
    {
      // a couple pages of code
    }
  // which for block is ending here?  A good text editor will tell you, 
  // but it's not obvious when you're reading the code
}

4
Tại sao bạn vẫn có một vài trang mã trong vòng lặp for của mình?
Adam Jaskiewicz

b / c Tôi là một người vô cảm, và tôi bị ám ảnh bởi việc loại bỏ "chi phí" của các cuộc gọi chức năng. :)
hung hăng

4
Thông thường, một vài trang mã phải ở trong một chức năng riêng biệt.
Jonathan Leffler

1
xin lỗi, ý tôi là tôi đang đóng vai trò của một cục như vậy. Tuy nhiên, tôi cho rằng vấn đề tương tự có thể xảy ra đối với các vùng ngắn hơn, khi được xem qua một khung nhìn nhỏ. Và đó là ngoài tầm kiểm soát của các lập trình viên.
hung hăng

7

Tôi đã từng là một người ủng hộ rất lớn cho "niềng răng xoăn là PHẢI!", Nhưng kể từ khi áp dụng thử nghiệm đơn vị, tôi thấy rằng các thử nghiệm đơn vị của tôi bảo vệ các tuyên bố không thể tránh khỏi các tình huống như:

if (foo)
    snafu();
    bar();

Với các bài kiểm tra đơn vị tốt, tôi có thể tự tin bỏ qua các dấu ngoặc nhọn cho các câu lệnh đơn giản để cải thiện khả năng đọc (vâng, điều đó có thể chủ quan).

Ngoài ra, đối với một cái gì đó như ở trên, tôi có thể sẽ giống như sau:

if (foo) snafu();

Bằng cách đó, nhà phát triển cần thêm thanh () vào điều kiện, sẽ có nhiều khả năng hơn để nhận ra việc thiếu dấu ngoặc nhọn và thêm chúng.


2
Bạn nói "Tôi không cần nó" sau đó đưa ra một lý do để sử dụng nó: khả năng đọc!
tvanfosson

4
Tôi sẽ không dựa vào Bài kiểm tra đơn vị để tìm ra điều đó. LINT có thể, Đơn vị kiểm tra không!
Kristen

2
@Kristen - Ý tưởng của bài kiểm tra đơn vị là để khẳng định hành vi của phương pháp. Nếu hành vi thay đổi (như được mô tả ở trên), bài kiểm tra đơn vị sẽ thất bại. Tôi không thấy vấn đề trong đó.
Liggy

Nhưng tại sao phải dựa vào Bài kiểm tra đơn vị để chọn ra điều hiển nhiên cần được sửa trước khi đến UT?
Andrew

7

Sử dụng một số đánh giá cá nhân.

if (foo)
  bar();

là tốt bởi chính nó. Trừ khi bạn thực sự lo lắng về việc biến thành một thứ như thế này sau này:

if (foo)
  bar();
  baz();

Nếu bạn không lo lắng về vấn đề đạo đức, bạn vẫn ổn (Tôi không - nếu họ không thể hiểu đúng cú pháp mã cơ bản, đây là vấn đề ít nhất của họ)>

Đổi lại, nó dễ đọc hơn nhiều.

Thời gian còn lại:

if (foo) {
  bar();
  baz();
}

Đó là yêu thích của tôi miễn là tôi có thể nhớ. Ngoài ra:

if (foo) {
  bar();
  baz();
} else {
  qux();
}

Làm việc cho tôi.

Không gian dọc tự nó không liên quan khủng khiếp, khả năng đọc là. Niềng răng mở trên một dòng tự nó chỉ dừng cuộc trò chuyện cho một yếu tố cú pháp, cho đến khi mắt bạn chuyển xuống dòng tiếp theo. Không phải những gì tôi thích.


Lần duy nhất tôi thích phong cách đầu tiên là khi tôi phải quay lại ngay lập tức hoặc đưa ra lỗi trong trường hợp có gì đó không đúng. Thích if (parameter == null) return null;.
nawfal

7

Được rồi, đây là một câu hỏi cũ đã được trả lời cho đến chết. Tôi có một cái gì đó để thêm.

Đầu tiên tôi chỉ cần nói SỬ DỤNG CÁC BRACES. Chúng chỉ có thể giúp dễ đọc và khả năng đọc (cho chính bạn và những người khác!) Sẽ rất cao trong danh sách ưu tiên của bạn trừ khi bạn đang viết lắp ráp. Mã không thể đọc được luôn, luôn dẫn đến lỗi. Nếu bạn thấy rằng niềng răng làm cho mã của bạn chiếm quá nhiều không gian, thì các phương thức của bạn có thể quá dài. Hầu hết hoặc tất cả các phương pháp sẽ phù hợp với một chiều cao màn hình nếu bạn thực hiện đúng và Tìm (F3) là bạn của bạn.

Bây giờ cho sự bổ sung của tôi: Có một vấn đề với điều này:

if (foo) bar();

Hãy thử thiết lập một điểm dừng sẽ chỉ được nhấn nếu thanh () sẽ chạy. Bạn có thể làm điều này trong C # bằng cách đặt con trỏ vào nửa sau của mã, nhưng điều đó không rõ ràng và hơi khó chịu. Trong C ++, bạn không thể làm điều đó cả. Một trong những nhà phát triển cao cấp nhất của chúng tôi làm việc về mã C ++ khăng khăng đòi chia các câu lệnh 'nếu' thành hai dòng vì lý do này. Và tôi đồng ý với anh ta.

Vì vậy, làm điều này:

if (foo)
{
    bar(); //It is easy to put a breakpoint here, and that is useful.
}

2
Nhấp chuột phải vào thanh và chọn Chèn Breakpoint ... Không khó chút nào. Biết công cụ của bạn.
Bob

Nhấp chuột phải vào thanh chỉ báo không làm gì cả; bạn có nghĩa là nhấp chuột phải vào văn bản? Dù sao, nếu bạn chỉ muốn điểm dừng được nhấn khi câu lệnh "if" là đúng và "bar ()" nằm trên dòng riêng của nó, bạn có thể đặt con trỏ ở bất cứ đâu trong dòng đó và nhấn F9 hoặc nhấp chuột trái vào lề chỉ số. Tuy nhiên, nếu tất cả mã nằm trên một dòng, bạn phải định vị con trỏ trên "bar ()" hoặc nhấp chuột phải chính xác vào đó trước khi nhấn F9 và nhấp vào lề chỉ báo sẽ không hoạt động (đặt điểm dừng trên ' nếu'). Nó không "cứng", nhưng nó đòi hỏi ít sự tập trung hơn khi mã nằm trên hai dòng.
đá

Ồ, ha ha, nhấp chuột phải vào "bar ()." Bạn đã có nghĩa là văn bản. Gotcha. Chắc chắn, hoặc chỉ cần nhấn F9 ...
đá

>> bạn phải định vị con trỏ trên "bar ()" hoặc nhấp chuột phải chính xác vào đó trước khi nhấn F9, (ý tôi là định vị con trỏ chính xác ở đó trước khi nhấn F9 hoặc nhấp chuột phải)
đá

7

để giữ cho mã với dấu ngoặc không chiếm nhiều dung lượng, tôi sử dụng kỹ thuật được đề xuất trong sách Hoàn thành mã :

if (...) {
    foo();
    bar();
}
else {
    ...
}

Tôi không hiểu tại sao nó bị giảm tốc độ, nó tiết kiệm một dòng cho mỗi cặp khung tôi sử dụng nó mọi lúc
Eric

2
Điều này phổ biến hơn được gọi là phong cách K & R. Dựa trên cuốn sách Ngôn ngữ lập trình C của Kernighan và Ritchie: en.wikipedia.org/wiki/The_C_Programming_L Language_(book ) . Và nó không nên làm bạn thất vọng.
jmucchiello

Cảm ơn! Tôi nghĩ rằng đó chỉ là sở thích cá nhân. Tôi đã từng thấy cú pháp này gây phiền nhiễu cho chính mình, nhưng tôi đã chấp nhận nó sau khi đọc Code Complete và thực sự thích nó ngay bây giờ.
Nathan Prather

tôi đã đọc tất cả các câu trả lời trên đây với suy nghĩ "tại sao chưa có ai đề xuất điều này ???" Nó làm cho khung đóng cửa thẳng hàng với khối mà nó đang đóng, đó là "nếu" hoặc "trong khi", v.v., không phải là "{" vô nghĩa. +1.
nickf

Nếu niềng răng mở luôn được đặt trên các dòng một mình, thì một dấu ifđược theo sau bởi một dòng thụt lề duy nhất có thể được nhận ra bằng mắt là kiểm soát một câu lệnh duy nhất mà không cần bất kỳ dấu ngoặc nào. Do đó, kiểu kết hợp mở do đó tiết kiệm khoảng trắng trong tình huống trong đó một ifcâu lệnh kiểm soát một cái gì đó về mặt ngữ nghĩa là một hành động duy nhất (khác với một chuỗi các bước xảy ra chỉ chứa một bước duy nhất). Ví dụ: if (someCondition)/ `ném một số ngoại lệ mới (...)`.
supercat

6

Giả sử bạn có một số mã:

if (foo)
    bar();

và sau đó một người khác đi cùng và thêm:

if (foo)
    snafu();
    bar();

Theo cách nó được viết, bar (); bây giờ được thực hiện vô điều kiện. Bằng cách bao gồm các dấu ngoặc nhọn, bạn ngăn ngừa loại lỗi vô ý này. Mã phải được viết theo cách để làm cho những sai lầm đó trở nên khó khăn hoặc không thể thực hiện được. Nếu tôi đang thực hiện đánh giá mã và thấy các dấu ngoặc nhọn bị thiếu, đặc biệt là trải rộng trên nhiều dòng, tôi sẽ tạo ra một khiếm khuyết. Trong trường hợp nó là hợp lý, hãy giữ nó trên một dòng để cơ hội gây ra lỗi như vậy một lần nữa được giữ ở mức tối thiểu.


Điểm đó đã được thực hiện trong câu hỏi.
Mike Scott

Không hoàn toàn, thực sự. Vâng, anh ấy đã đề cập đến tiềm năng của loại lỗi này, nhưng tôi đã nói về việc làm cho mã của bạn chống lỗi và loại bỏ khả năng lỗi là một phần của các thực tiễn tốt nhất.
Elie

Có thực sự là một thứ như mã bằng chứng lỗi?
Bob

Không, nhưng bạn có thể làm cho nó khó khăn hơn. Đọc "Bí mật tốt nhất về đánh giá mã ngang hàng" của Jason Cohen (xem liên kết ở bên cạnh một số trang ở đây) để hiểu rõ hơn về lý do tại sao bạn làm điều này.
Elie

Mike Scott này là ai và tại sao anh ta là cảnh sát trả lời? Nó luôn làm tôi khó chịu khi mọi người phải nói rõ điều gì. Vậy tại sao Mike không tiếp tục bình luận về mô tả được thêm mà người dùng đang đăng về vấn đề này. Không phải tất cả các câu trả lời đề cập đến câu hỏi?
bruceatk

6

Giảm dòng không thực sự là một lý lẽ tốt cho việc bỏ niềng răng. Nếu phương pháp của bạn quá lớn, có lẽ nên tái cấu trúc thành các phần nhỏ hơn hoặc được cấu trúc lại. Làm như vậy chắc chắn sẽ tăng khả năng đọc hơn là chỉ đơn giản là tháo niềng răng.


5

Tôi luôn bỏ qua chúng khi thích hợp, chẳng hạn như trong ví dụ đầu tiên của bạn. Mã sạch, súc tích Tôi có thể thấy và hiểu bằng cách liếc nhìn dễ bảo trì, gỡ lỗi và hiểu hơn mã tôi phải cuộn qua và đọc từng dòng một. Tôi nghĩ rằng hầu hết các lập trình viên sẽ đồng ý với điều này.

Thật dễ dàng để nó ra khỏi tầm tay nếu bạn bắt đầu thực hiện nhiều lần lồng, nếu / các mệnh đề khác, nhưng tôi nghĩ rằng hầu hết các lập trình viên sẽ có thể biết nơi để vẽ đường thẳng.

Tôi thấy nó giống như các đối số cho if ( foo == 0 )vs if ( 0 == foo ). Cái sau có thể ngăn lỗi cho các lập trình viên mới (và thậm chí đôi khi cho cả các cựu chiến binh), trong khi cái trước dễ đọc và hiểu nhanh hơn khi bạn đang duy trì mã.


4

Hầu hết thời gian, nó đã ăn sâu như một tiêu chuẩn mã hóa, cho dù là cho một công ty hay một dự án FOSS.

Cuối cùng, một người khác sẽ cần phải mò mẫm mã của bạn và việc tiết kiệm thời gian cho mỗi nhà phát triển là phải tìm ra phong cách cụ thể của phần mã họ đang làm việc.

Ngoài ra, hãy tưởng tượng rằng ai đó đang đi giữa ngôn ngữ Python và ngôn ngữ Cish nhiều hơn một lần một ngày ... Trong thụt lề Python là một phần của biểu tượng khối của ngôn ngữ và sẽ khá dễ mắc lỗi như bạn trích dẫn.


+1 cho nhận xét về trăn. Tôi luôn ngạc nhiên về mức độ "ngu ngốc" của mình khi liên tục chuyển đổi giữa các ngôn ngữ.
Kena

3

Err về phía an toàn hơn - chỉ một lỗi nữa mà bạn có khả năng sẽ không phải sửa.

Cá nhân tôi cảm thấy an toàn hơn nếu tất cả các khối của tôi được bọc trong xoăn. Ngay cả đối với một lớp lót, đây là những ký hiệu đơn giản dễ dàng ngăn ngừa sai lầm. Nó làm cho mã dễ đọc hơn theo nghĩa là bạn thấy rõ những gì trong khối là không nhầm lẫn phần thân của khối với các câu lệnh sau bên ngoài khối.

Nếu tôi có một lớp lót, tôi thường định dạng nó như sau:

if( some_condition ) { do_some_operation; }

Nếu dòng quá rườm rà thì hãy sử dụng như sau:

if( some_condition )
{
    do_some_operation;
}
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.