Tại sao C # cho phép {} khối mã mà không có câu lệnh trước?


86

Tại sao C # cho phép khối mã mà không có một tuyên bố trước đó (ví dụ như if, else, for, while)?

void Main()
{
    {   // any sense in this?
        Console.Write("foo");
    }
}

70
Có lý do gì nó không nên ?
Jamiec

30
Nhưng như câu trả lời cho thấy nó có nhiều ý nghĩa hơn là "không đau, vì vậy hãy cho phép nó". Đây là một điểm đặt câu hỏi như vậy.
Seldon

8
1 câu hỏi nghe có vẻ ngây thơ, nhưng câu trả lời đã thực sự đã dạy cho tôi một cái gì đó có giá trị
Nicolas78

7
@Akash: Nếu không hỏi tại sao, chúng tôi sẽ không bao giờ trở nên tốt hơn.
richard

7
@Akash: Đó có vẻ như là một thái độ cầu kỳ. Mọi người luôn có những câu hỏi, và nếu không có lý do gì để hỏi tại sao, thì chẳng ích gì khi có SO. Trang web này không quá để mọi người giải quyết các vấn đề tức thì của chúng tôi (mặc dù điều đó xảy ra), nhưng để cung cấp một kho câu hỏi và câu trả lời sẽ giúp tất cả chúng ta trở thành những lập trình viên giỏi hơn. VẬY là để hỏi TẠI SAO! :-)
richard

Câu trả lời:


90

Trong bối cảnh bạn đưa ra, không có ý nghĩa. Việc ghi một chuỗi hằng vào bảng điều khiển sẽ hoạt động theo cùng một cách ở bất kỳ đâu trong luồng chương trình. 1

Thay vào đó, bạn thường sử dụng chúng để hạn chế phạm vi của một số biến cục bộ. Điều này được làm rõ hơn ở đâyở đây . Hãy xem câu trả lời của João Angelocâu trả lời của Chris Wallis để biết các ví dụ ngắn gọn. Tôi tin rằng điều tương tự cũng áp dụng cho một số ngôn ngữ khác có cú pháp kiểu C, không phải là chúng sẽ liên quan đến câu hỏi này.


1 Tất nhiên, trừ khi bạn quyết định cố gắng trở nên hài hước và tạo ra Consolelớp học của riêng mình , bằng một Write()phương pháp hoàn toàn bất ngờ.


24
Re: các ngôn ngữ khác: thông thường, nhưng không phải luôn luôn. JavaScript là một ngoại lệ đáng chú ý; khai báo một biến cục bộ trong một khối cụ thể không phân bổ biến đó cho khối.
Eric Lippert

@EricLippert: Trong C #, việc khai báo một biến trong một khối có khiến biến đó được xác định phạm vi theo quan điểm của thời gian chạy không? Từ những gì tôi có thể nói, vòng đời của biến thường không bị giới hạn bởi các khối xác định phạm vi, một thực tế có thể quan sát được trong các dự án ngôn ngữ hỗn hợp nếu điều đầu tiên được thực hiện với một biến trong vòng lặp là chuyển nó dưới dạng outtham số cho một triển khai giao diện được viết bằng một ngôn ngữ khác và việc triển khai đó đọc biến trước khi viết nó.
supercat

Và trong C ++, vì RAII thay vì GC, nó hữu ích hơn nhiều.
Deduplicator

Trong C ++, nó hoạt động giống như một khối try / catch, vì vậy các biến và lớp được khai báo trong phạm vi đó bật ra khỏi ngăn xếp khi bạn rời khỏi phạm vi đó. Điều này giúp ngăn chặn xung đột tên và giảm dung lượng bộ nhớ của hàm. Nó cũng loại bỏ các biến đó khỏi tự động hoàn thành của trình soạn thảo mã. Thường thì tôi thấy khối try / catch hữu ích hơn.
Kit10

144

Các { ... }có ít nhất là tác dụng phụ của việc giới thiệu một phạm vi mới cho các biến địa phương.

Tôi có xu hướng sử dụng chúng trong các switchcâu lệnh để cung cấp phạm vi khác nhau cho từng trường hợp và theo cách này, cho phép tôi xác định biến cục bộ có cùng tên ở vị trí sử dụng chúng gần nhất có thể và cũng để biểu thị rằng chúng chỉ hợp lệ ở cấp trường hợp.



6
Nếu bạn cần phạm vi trong trường hợp của mình, chúng quá lớn! Phương pháp chiết xuất.
Jay Bazuzi

20
@Jay Bazuzi, chỉ vì tôi muốn có một biến cục bộ có cùng tên trong nhiều trường hợp không có nghĩa là chúng phải lớn. Bạn chỉ cần đưa ra kết luận lớn ... :)
João Angelo

Xin chúc mừng huy hiệu Câu trả lời tuyệt vời của bạn! :)
BoltClock

1
@BoltClock, cảm ơn, tôi chỉ cần thêm một số câu hỏi về {}việc giữ cho những huy hiệu vàng đó đến ... :)
João Angelo

56

Nó không phải là một tính năng của C # nhiều hơn nó là một tác dụng phụ hợp lý của nhiều ngôn ngữ cú pháp C sử dụng dấu ngoặc nhọn để xác định phạm vi .

Trong ví dụ của bạn, dấu ngoặc nhọn không có tác dụng gì cả, nhưng trong đoạn mã sau, chúng xác định phạm vi và do đó khả năng hiển thị, của một biến:

Điều này được cho phép vì tôi nằm ngoài phạm vi trong khối đầu tiên và được xác định lại trong phần tiếp theo:

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

Điều này không được phép vì tôi đã vượt ra khỏi phạm vi và không còn hiển thị trong phạm vi bên ngoài:

{
    {
        int i = 0;
    }

    i = 1;
}

Và vân vân.


1
Tôi đã đọc về sự khác biệt trong danh pháp dấu ngoặc trong các loại tiếng Anh khác nhau, nhưng hương vị nào {}được gọi là dấu ngoặc đơn?
BoltClock

Câu hỏi hay! Tôi đoán rằng một người cố vấn của tôi đã gieo nó vào não tôi một thập kỷ trước. Tôi có lẽ nên gọi chúng là 'dấu ngoặc nhọn' hoặc 'dấu ngoặc nhọn'. Mỗi người cho riêng mình ... en.wikipedia.org/wiki/…
Chris Wallis

10
Trong vocab của tôi '(' = ngoặc đơn, '{' = dấu ngoặc nhọn, '[' = dấu ngoặc vuông
pb.

Chờ đã, tôi sẽ gọi chúng là một dấu ngoặc nhọn, và Wikipedia bao gồm cách đặt tên đó. Từ điển tiếng Anh Oxford định nghĩa cách sử dụng dấu ngoặc này là: One of two marks of the form [ ] or ( ), and in mathematical use also {}, used for enclosing a word or number of words, a portion of a mathematical formula, or the like, so as to separate it from the context; Trong mọi trường hợp, chúng không phải là dấu ngoặc đơn, nhưng 'dấu ngoặc nhọn' có vẻ OK.
câm vào

IME, "dấu ngoặc nhọn" và "dấu ngoặc vuông".
cp.engr 14/09/2016

18

Tôi coi {}như một câu lệnh có thể chứa một số câu lệnh.

Hãy xem xét một câu lệnh if tồn tại ngoài một biểu thức boolean được theo sau bởi một câu lệnh. Điều này sẽ hoạt động:

if (true) Console.Write("FooBar");

Điều này cũng sẽ hoạt động:

if (true)
{
  Console.Write("Foo");
  Console.Write("Bar");
}

Nếu tôi không nhầm thì đây được gọi là câu lệnh khối.

{}có thể chứa các câu lệnh khác nó cũng có thể chứa các câu lệnh khác {}. Phạm vi của một biến được xác định bởi cha của nó {}(câu lệnh khối).

Điểm mà tôi đang cố gắng đưa ra là đó {}chỉ là một tuyên bố, vì vậy nó không yêu cầu nếu hoặc bất cứ điều gì ...


+1 Đó chính xác là dấu ngoặc nhọn trong ngôn ngữ kiểu C - một cái gọi là "câu lệnh ghép".
Michael Ekstrand

12

Quy tắc chung trong các ngôn ngữ cú pháp C là "bất kỳ thứ gì ở giữa { }phải được coi là một câu lệnh duy nhất và nó có thể đi đến bất cứ nơi nào mà một câu lệnh duy nhất có thể":

  • Sau một if.
  • Sau một for, whilehoặc do.
  • Bất cứ nơi nào trong mã .

Đối với tất cả các ý định và mục đích, ngữ pháp ngôn ngữ đã bao gồm điều này:

     <statement> :== <definition of valid statement> | "{" <statement-list> "}"
<statement-list> :== <statement> | <statement-list> <statement>

Đó là, "một câu lệnh có thể được cấu tạo bởi (nhiều thứ khác nhau) hoặc một dấu ngoặc nhọn mở, theo sau là một danh sách câu lệnh (có thể bao gồm một hoặc nhiều câu lệnh), theo sau là một dấu ngoặc nhọn". IE "một { }khối có thể thay thế bất kỳ câu lệnh nào, ở bất kỳ đâu". Bao gồm cả ở giữa mã.

Việc không cho phép một { }khối ở bất kỳ đâu mà một câu lệnh đơn lẻ có thể thực sự làm cho định nghĩa ngôn ngữ trở nên phức tạp hơn .


Ai đó vừa nâng câu trả lời này (sau 9 năm), đây thực sự không phải là câu trả lời cụ thể cho câu hỏi này, mà là một cuộc thảo luận chung chung. Nó có thể rất tốt đã được thay thế bởi ngôn ngữ và thư viện .. Nhưng dù sao cũng rất vui, cảm ơn bạn.
Massimo

1

Vì C ++ (và java) cho phép các khối mã mà không có câu lệnh trước.

C ++ cho phép chúng vì C đã làm.

Có thể nói tất cả xuất phát từ thực tế là thiết kế ngôn ngữ chương trình Hoa Kỳ (dựa trên C) đã chiến thắng hơn là thiết kế ngôn ngữ chương trình Châu Âu ( dựa trên Modula-2 ).

(Các câu lệnh điều khiển hoạt động trên một câu lệnh duy nhất, các câu lệnh có thể là nhóm để tạo ra các câu lệnh mới)


Ý bạn là Module2 hay Modula2?
Richard Ev

Thật vậy, đây là câu trả lời chính xác. Hơn nữa, tôi chỉ trả lời "Bởi vì mọi ngôn ngữ máy tính đều có."
Fattie,


0

1 Bởi vì ... Nó Duy trì Vùng Phạm vi của câu lệnh .. hoặc Chức năng, Điều này thực sự hữu ích cho việc điều chỉnh mã lớn ..

{
    {
        // Here this 'i' is we can use for this scope only and out side of this scope we can't get this 'i' variable.

        int i = 0;
    }

    {
        int i = 0;
    }
}

nhập mô tả hình ảnh ở đây


0

Bạn đã hỏi "tại sao" C # cho phép khối mã mà không cần câu lệnh trước. Câu hỏi "tại sao" cũng có thể được hiểu là "lợi ích có thể có của cấu trúc này là gì?"

Cá nhân tôi sử dụng các khối mã không cần câu lệnh trong C # nơi mà khả năng đọc được cải thiện đáng kể cho các nhà phát triển khác, đồng thời lưu ý rằng khối mã giới hạn phạm vi của các biến cục bộ. Ví dụ: hãy xem xét đoạn mã sau, đoạn mã này dễ đọc hơn rất nhiều nhờ các khối mã bổ sung:

OrgUnit world = new OrgUnit() { Name = "World" };
{
    OrgUnit europe = new OrgUnit() { Name = "Europe" };
    world.SubUnits.Add(europe);
    {
        OrgUnit germany = new OrgUnit() { Name = "Germany" };
        europe.SubUnits.Add(germany);

        //...etc.
    }
}
//...commit structure to DB here

Tôi biết rằng điều này có thể được giải quyết một cách thanh lịch hơn bằng cách sử dụng các phương pháp cho từng cấp cấu trúc. Tuy nhiên, một lần nữa, hãy nhớ rằng những thứ như trình gieo dữ liệu mẫu thường cần nhanh chóng.

Vì vậy, mặc dù đoạn mã trên được thực thi một cách tuyến tính, cấu trúc mã vẫn đại diện cho cấu trúc "thế giới thực" của các đối tượng, do đó giúp các nhà phát triển khác hiểu, duy trì và mở rộng dễ dàng hơ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.