เหตุใด C # จึงอนุญาตให้ {} บล็อกรหัสโดยไม่มีคำสั่งก่อนหน้า


86

ทำไม C # ให้รหัสที่บล็อกโดยไม่ต้องมีคำสั่งก่อนหน้านี้ (เช่นif, else, for, while)?

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

70
มีเหตุผลอะไรที่ไม่ควร ?
Jamiec

30
แต่เนื่องจากคำตอบแสดงให้เห็นว่ามันมีความหมายมากกว่าแค่ "ไม่เจ็บก็ยอม" นี่คือประเด็นของการถามคำถามดังกล่าว
Seldon

8
คำถาม +1 ฟังดูไร้เดียงสาเพียงพอ แต่คำตอบสอนสิ่งที่มีค่าแก่ฉันจริงๆ
Nicolas78

7
@ Akash: โดยไม่ถามว่าทำไมเราจะไม่ดีขึ้น
richard

7
@ Akash: ดูเหมือนว่าจะเป็นทัศนคติของชนชั้นสูง ผู้คนมักจะมีคำถามเสมอและหากไม่มีเหตุผลที่จะถามว่าทำไมก็ไม่มีเหตุผลที่จะมี SO ไซต์นี้ไม่ได้มีไว้สำหรับการแก้ปัญหาเฉพาะหน้าของเรามากนัก (แม้ว่าจะเกิดขึ้น) แต่เพื่อให้มีที่เก็บคำถามและคำตอบที่จะช่วยให้เราทุกคนเป็นโปรแกรมเมอร์ที่ดีขึ้น ดังนั้นถามว่าทำไม! :-)
richard

คำตอบ:


90

ในบริบทที่คุณให้นั้นไม่มีความสำคัญ การเขียนสตริงคงที่ไปยังคอนโซลจะทำงานในลักษณะเดียวกันกับที่ใดก็ได้ในผังงานโปรแกรม 1

โดยทั่วไปคุณจะใช้เพื่อ จำกัด ขอบเขตของตัวแปรท้องถิ่นบางตัว นี้จะชี้แจงเพิ่มเติมที่นี่และที่นี่ ดูคำตอบของJoão AngeloและคำตอบของChris Wallisสำหรับตัวอย่างสั้น ๆ ฉันเชื่อว่าเช่นเดียวกันกับภาษาอื่น ๆ ที่มีไวยากรณ์สไตล์ C เช่นกันไม่ใช่ว่าจะเกี่ยวข้องกับคำถามนี้


1 เว้นแต่ว่าคุณตัดสินใจที่จะพยายามทำตัวตลกและสร้างConsoleชั้นเรียนของคุณเองด้วยWrite()วิธีการที่ทำสิ่งที่คาดไม่ถึงอย่างสิ้นเชิง


24
Re: ภาษาอื่น ๆ : ปกติ แต่ไม่เสมอไป JavaScript เป็นข้อยกเว้นที่น่าสังเกต การประกาศตัวแปรโลคัลในบล็อกใดบล็อกหนึ่งไม่ได้กำหนดขอบเขตของตัวแปรให้กับบล็อก
Eric Lippert

@EricLippert: ใน C # การประกาศตัวแปรภายในบล็อกทำให้ตัวแปรถูกกำหนดขอบเขตจากมุมมองของรันไทม์หรือไม่? จากสิ่งที่ฉันสามารถบอกได้ว่าช่วงชีวิตของตัวแปรมักจะไม่ถูก จำกัด ด้วยบล็อกขอบเขตซึ่งเป็นความจริงที่สังเกตได้ในโครงการภาษาผสมหากสิ่งแรกที่ทำกับตัวแปรในลูปคือการส่งผ่านเป็นoutพารามิเตอร์ไปยังการใช้งานอินเทอร์เฟซที่เขียนขึ้น ในภาษาอื่นและการนำไปใช้งานนั้นอ่านตัวแปรก่อนที่จะเขียน
supercat

และในภาษา C ++ เนื่องจาก RAII แทนที่จะเป็น GC จึงมีประโยชน์มากกว่า
Deduplicator

ใน C ++ จะทำหน้าที่คล้ายกับบล็อก try / catch ดังนั้นตัวแปรและคลาสที่ประกาศภายในขอบเขตนั้นจะหลุดออกจากสแต็กเมื่อคุณออกจากขอบเขตนั้น สิ่งนี้ช่วยป้องกันการชนชื่อและลดการใช้หน่วยความจำของฟังก์ชัน นอกจากนี้ยังลบตัวแปรเหล่านั้นออกจากการเติมข้อความอัตโนมัติของโปรแกรมแก้ไขโค้ด บ่อยครั้งที่ฉันพบว่าบล็อก try / catch มีประโยชน์มากกว่า
Kit10

144

{ ... }มีอย่างน้อยผลข้างเคียงของการแนะนำขอบเขตใหม่สำหรับตัวแปรท้องถิ่น

ฉันมักจะใช้มันในswitchคำสั่งเพื่อให้ขอบเขตที่แตกต่างกันสำหรับแต่ละกรณีและด้วยวิธีนี้ทำให้ฉันสามารถกำหนดตัวแปรท้องถิ่นด้วยชื่อเดียวกันในตำแหน่งที่ใกล้เคียงที่สุดเท่าที่จะเป็นไปได้ของการใช้งานของพวกเขาและยังแสดงว่าพวกเขาใช้ได้เฉพาะในระดับเคสเท่านั้น



6
หากคุณต้องการขอบเขตในกรณีของคุณมันใหญ่เกินไป! วิธีการแยก
Jay Bazuzi

20
@Jay Bazuzi เพียงเพราะฉันต้องการมีตัวแปรท้องถิ่นที่มีชื่อเดียวกันมากกว่าหนึ่งกรณีไม่ได้หมายความว่าพวกเขาจะต้องใหญ่โต คุณแค่กระโดดไปสู่ข้อสรุปที่ยิ่งใหญ่ ... :)
João Angelo

ขอแสดงความยินดีกับป้ายคำตอบที่ยอดเยี่ยมของคุณ! :)
BoltClock

1
@BoltClock ขอบคุณฉันแค่ต้องการคำถามเพิ่มเติมเกี่ยวกับ{}การเก็บป้ายทองเหล่านั้นไว้ ... :)
João Angelo

56

มันเป็นไม่มากคุณลักษณะของ C # กว่าที่มันเป็นตรรกะผลข้างเคียงของหลายภาษา C ไวยากรณ์ที่ใช้จัดฟันเพื่อกำหนดขอบเขต

ในตัวอย่างของคุณวงเล็บปีกกาจะไม่มีผลเลย แต่ในโค้ดต่อไปนี้จะกำหนดขอบเขตและดังนั้นการมองเห็นของตัวแปร:

สิ่งนี้ได้รับอนุญาตเมื่อฉันอยู่นอกขอบเขตในบล็อกแรกและถูกกำหนดอีกครั้งในถัดไป

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

สิ่งนี้ไม่ได้รับอนุญาตเนื่องจากฉันอยู่นอกขอบเขตและไม่สามารถมองเห็นได้อีกต่อไปในขอบเขตภายนอก:

{
    {
        int i = 0;
    }

    i = 1;
}

และอื่น ๆ ไปเรื่อย ๆ


1
ฉันได้อ่านเกี่ยวกับความไม่เสมอภาคในการตั้งชื่อวงเล็บในภาษาอังกฤษหลายรสชาติ แต่รสชาติใดที่{}เรียกว่าวงเล็บ
BoltClock

คำถามที่ดี! ฉันเดาว่าที่ปรึกษาของฉันปลูกมันไว้ในสมองของฉันเมื่อสิบปีก่อน ฉันน่าจะเรียกมันว่า "วงเล็บปีกกา" หรือ "วงเล็บปีกกา" แต่ละคนเป็นของตัวเอง… en.wikipedia.org/wiki/
Chris Wallis

10
ในคำศัพท์ของฉัน '(' = parenthesis, '{' = brace, '[' = วงเล็บเหลี่ยม
pb

เดี๋ยวก่อนฉันจะเรียกมันว่าวงเล็บปีกกาและ Wikipedia ก็รวมการตั้งชื่อนั้นไว้ด้วย พจนานุกรมภาษาอังกฤษออกซ์ฟอร์ดให้คำจำกัดความการใช้วงเล็บนี้ว่า: 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; ในกรณีใดก็ตามพวกเขาไม่ใช่วงเล็บ แต่ 'วงเล็บปีกกา' ดูเหมือนจะใช้ได้
dumbledad

IME, "วงเล็บปีกกา" และ "วงเล็บเหลี่ยม"
cp.engr

18

ฉันถือว่า{}เป็นคำสั่งที่สามารถมีหลายข้อความ

พิจารณาคำสั่ง if ที่อยู่นอกนิพจน์บูลีนตามด้วยหนึ่งคำสั่ง สิ่งนี้จะได้ผล:

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

สิ่งนี้จะใช้ได้เช่นกัน:

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

ถ้าผมจำไม่ผิดเรียกว่า block statement

เนื่องจาก{}สามารถมีงบอื่น ๆ {}ก็ยังสามารถมีอื่น ๆ ขอบเขตของตัวแปรถูกกำหนดโดยพาเรนต์{}(คำสั่งบล็อก)

ประเด็นที่ฉันพยายามจะทำให้เป็น{}เพียงคำสั่งดังนั้นจึงไม่ต้องใช้ if หรืออะไรก็ตาม ...


+1 นั่นคือวงเล็บปีกกาในภาษาสไตล์ C ซึ่งเรียกว่า "คำสั่งผสม"
Michael Ekstrand

12

กฎทั่วไปในภาษา C-syntax คือ "สิ่งใด ๆ ระหว่าง{ }ควรถือว่าเป็นคำสั่งเดียวและสามารถไปได้ทุกที่ที่คำสั่งเดียวทำได้":

  • หลังจากif.
  • หลังจากfor, หรือwhiledo
  • ใดในรหัส

สำหรับความตั้งใจและวัตถุประสงค์ทั้งหมดเป็นไปตามที่ไวยากรณ์ของภาษารวมไว้:

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

นั่นคือ "คำสั่งสามารถประกอบด้วย(สิ่งต่างๆ)หรือวงเล็บปีกกาเปิดตามด้วยรายการคำสั่ง (ซึ่งอาจรวมถึงหนึ่งคำสั่งหรือมากกว่า) ตามด้วยวงเล็บปีกกาปิด" IE " { }บล็อกสามารถแทนที่คำสั่งใดก็ได้ทุกที่" รวมถึงตรงกลางของรหัส

การไม่อนุญาตให้{ }บล็อกทุกที่ที่คำสั่งเดียวสามารถไปได้จะทำให้คำจำกัดความภาษาซับซ้อนขึ้น


มีคนเพิ่มคำตอบนี้ (หลังจาก 9 ปี) ซึ่งไม่ใช่คำตอบที่เฉพาะเจาะจงสำหรับคำถามนี้ แต่เป็นการสนทนาทั่วไปมากกว่า มันอาจจะถูกแทนที่ด้วยภาษาและห้องสมุดได้เป็นอย่างดี .. แต่มันก็เป็นเรื่องที่น่ายินดีขอบคุณ
Massimo

1

เนื่องจาก C ++ (และ java) อนุญาตให้บล็อกโค้ดโดยไม่มีคำสั่งนำหน้า

C ++ อนุญาตเพราะ C ทำ

คุณสามารถพูดได้ว่าทั้งหมดมาจากข้อเท็จจริงที่ว่าการออกแบบภาษาโปรแกรมของสหรัฐอเมริกา (ตาม C) ได้รับรางวัลมากกว่าการออกแบบภาษาโปรแกรมยุโรป (ตามModula-2 )

(คำสั่งควบคุมทำหน้าที่ในคำสั่งเดียวคำสั่งสามารถเป็นกลุ่มเพื่อสร้างคำสั่งใหม่ได้)


คุณหมายถึง Module2 หรือ Modula2?
Richard Ev

แน่นอนนี่คือคำตอบที่ถูกต้อง นอกจากนี้ฉันเพียงแค่ตอบว่า "เพราะทุกภาษาคอมพิวเตอร์ทำได้"
Fattie


0

1เพราะ ... มันคงไว้ซึ่งขอบเขตของคำสั่ง .. หรือฟังก์ชั่นนี้มีประโยชน์มากสำหรับแผงคอรหัสขนาดใหญ่ ..

{
    {
        // 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;
    }
}

ป้อนคำอธิบายภาพที่นี่


0

คุณถามว่า "ทำไม" C # อนุญาตให้บล็อกโค้ดโดยไม่ต้องมีข้อความนำหน้า คำถาม "ทำไม" ยังสามารถตีความได้ว่า "สิ่งที่เป็นไปได้ประโยชน์ของโครงสร้างนี้?"

โดยส่วนตัวแล้วฉันใช้บล็อกโค้ดแบบไม่มีคำสั่งใน C # ซึ่งความสามารถในการอ่านได้รับการปรับปรุงให้ดีขึ้นอย่างมากสำหรับนักพัฒนารายอื่นในขณะที่โปรดทราบว่าการบล็อกโค้ด จำกัด ขอบเขตของตัวแปรภายใน ตัวอย่างเช่นพิจารณาข้อมูลโค้ดต่อไปนี้ซึ่งอ่านได้ง่ายกว่ามากเนื่องจากบล็อกโค้ดเพิ่มเติม:

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

ฉันทราบดีว่าสิ่งนี้สามารถแก้ไขได้อย่างหรูหรามากขึ้นโดยใช้วิธีการสำหรับโครงสร้างแต่ละระดับ แต่โปรดทราบอีกครั้งว่าสิ่งต่างๆเช่นตัวเพาะข้อมูลตัวอย่างมักจะต้องรวดเร็ว

ดังนั้นแม้ว่าโค้ดด้านบนจะดำเนินการแบบเชิงเส้น แต่โครงสร้างโค้ดจะแสดงถึงโครงสร้าง "โลกแห่งความจริง" ของอ็อบเจ็กต์ซึ่งทำให้นักพัฒนารายอื่นเข้าใจดูแลรักษาและขยายได้ง่ายขึ้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.