เหตุใด C # จึงไม่อนุญาตให้ใช้ตัวแปรโลคัลแบบอ่านอย่างเดียว


116

มีการถกเถียงอย่างเป็นกันเองกับเพื่อนร่วมงานเกี่ยวกับเรื่องนี้ เรามีความคิดบางอย่างเกี่ยวกับเรื่องนี้ แต่สงสัยว่ากลุ่ม SO คิดอย่างไรกับเรื่องนี้?


4
@ColonelPanic C และ C ++ มีตัวแปรโลคัล const ซึ่งคุณสามารถเริ่มต้นด้วยค่าที่คำนวณรันไทม์
Crashworks

1
JavaScript 2015 (ES6) มีประเภท const เช่น {const myList = [1,2,3]; } เป็นการฝึกเขียนโปรแกรมที่ดีมากในการใช้โครงสร้างนี้ ข้อมูลเพิ่มเติม: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox

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

1
ไม่ใช่แค่ปัญหาด้านภาษาเท่านั้น เป็นปัญหาของคนส่วนใหญ่ในชุมชน C # รวมถึงผู้เชี่ยวชาญด้าน C # ที่ได้รับคะแนนสูงสุดโดยที่พวกเขาไม่สนใจเกี่ยวกับความถูกต้องของ const และสิ่งที่เกี่ยวข้อง การต่อต้านนั้นไร้ประโยชน์
Patrick Fromberg

1
อัปเดต 2017 : โปรดลงคะแนนสำหรับคำขอคุณสมบัติภายใต้การสนทนาในที่เก็บ C # Language Design! github.com/dotnet/csharplang/issues/188
Colonel Panic

คำตอบ:


14

เหตุผลหนึ่งคือไม่มีการรองรับ CLR สำหรับโลคัลแบบอ่านอย่างเดียว อ่านอย่างเดียวถูกแปลเป็นรหัส opcode เริ่มต้นของ CLR / CLI แฟล็กนี้สามารถใช้ได้กับฟิลด์เท่านั้นและไม่มีความหมายสำหรับโลคัล ในความเป็นจริงการนำไปใช้กับท้องถิ่นอาจทำให้เกิดรหัสที่ไม่สามารถตรวจสอบได้

นี่ไม่ได้หมายความว่า C # ไม่สามารถทำได้ แต่มันจะให้สองความหมายที่แตกต่างกันสำหรับโครงสร้างภาษาเดียวกัน เวอร์ชันสำหรับชาวบ้านจะไม่มีการแมปที่เทียบเท่ากับ CLR


57
จริงๆแล้วมันไม่มีอะไรเกี่ยวข้องกับการรองรับ CLI สำหรับฟีเจอร์นี้เนื่องจากตัวแปรโลคัลไม่ได้สัมผัสกับแอสเซมบลีอื่น ๆ readonlyคำหลักสำหรับเขตข้อมูลความต้องการที่จะได้รับการสนับสนุนโดย CLI เพราะผลของมันคือสามารถมองเห็นได้ประกอบอื่น ๆ ทั้งหมดนี้หมายความว่าตัวแปรมีการกำหนดเพียงครั้งเดียวในเมธอดในเวลาคอมไพล์
Sam Harwell

16
ฉันคิดว่าคุณเพิ่งเปลี่ยนคำถามว่าทำไม CLR ไม่สนับสนุนสิ่งนี้แทนที่จะให้เหตุผลเบื้องหลัง ไม่อนุญาตให้มีคนท้องถิ่นดังนั้นจึงเป็นเรื่องสมเหตุสมผลที่จะคาดหวังให้คนท้องถิ่นอ่านอย่างเดียวเช่นกัน
Chad Schouggins

9
ตัวอย่างนี้เป็นตัวแปรที่กำหนดในคำสั่งใช้ เป็นแบบโลคัล ... และอ่านอย่างเดียว (พยายามกำหนด C # จะเพิ่มข้อผิดพลาด)
Softlion

7
-1 ใน C ++ ไม่มีการรองรับรหัสเครื่องconst(ซึ่งใน C ++ คล้าย C # readonlyมากกว่าเหมือน C # constแม้ว่าจะสามารถเล่นได้ทั้งสองบทบาทก็ตาม) ยังรองรับ C ++ constสำหรับตัวแปรอัตโนมัติในเครื่อง ดังนั้นการขาดการสนับสนุน CLR สำหรับ C # readonlyสำหรับตัวแปรโลคัลจึงไม่เกี่ยวข้อง
ไชโยและ hth - Alf

5
1. สิ่งนี้สามารถเป็นคุณสมบัติคอมไพเลอร์ได้อย่างง่ายดายเช่นเดียวกับใน C ++ การสนับสนุน CLR ไม่เกี่ยวข้องโดยสิ้นเชิง การประกอบเครื่องก็ไม่รองรับด้วยแล้วไงล่ะ? 2. (มันจะ) สร้างรหัสที่ไม่สามารถตรวจสอบได้ - ฉันไม่เห็นวิธีการ แต่บางทีฉันอาจเข้าใจผิด 3. มันจะให้สองความหมายที่แตกต่างกันสำหรับโครงสร้างภาษาเดียวกัน - ฉันสงสัยว่าจะมีใครมองว่านี่เป็นปัญหาเนื่องจากusingและoutกำลังทำอย่างนั้นและโลกก็ไม่ได้พังทลาย
Lou

67

ฉันคิดว่ามันเป็นการตัดสินที่ไม่ดีในส่วนของสถาปนิก C # ตัวแก้ไขแบบอ่านอย่างเดียวบนตัวแปรในระบบช่วยรักษาความถูกต้องของโปรแกรม (เช่นเดียวกับการยืนยัน) และอาจช่วยให้คอมไพเลอร์ปรับแต่งโค้ดได้ดีที่สุด (อย่างน้อยก็ในกรณีของภาษาอื่น ๆ ) ความจริงที่ว่ามันไม่ได้รับอนุญาตใน C # ในขณะนี้เป็นอีกหนึ่งข้อโต้แย้งที่ว่า "คุณสมบัติ" บางอย่างของ C # เป็นเพียงการบังคับใช้รูปแบบการเข้ารหัสส่วนบุคคลของผู้สร้างเท่านั้น


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

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

คอมไพเลอร์สมัยใหม่ไม่ทำการกำหนดแบบคงที่หรือไม่? ในกรณีนี้มันเป็นเรื่องเล็กน้อยเท่าที่เกี่ยวข้องกับการปรับให้เหมาะสม (แต่ถ้าคอมไพเลอร์รองรับ SSA ก็หมายความว่าการใช้ตัวแปรโลคัลกำหนดครั้งเดียวก็ไม่สำคัญเช่นกัน)
Dai

33

ในการตอบคำตอบของ Jared มันอาจจะต้องเป็นคุณสมบัติเวลาคอมไพล์ - คอมไพเลอร์จะห้ามไม่ให้คุณเขียนถึงตัวแปรหลังจากการประกาศครั้งแรก (ซึ่งจะต้องรวมการมอบหมาย)

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

สำหรับสิ่งที่คุ้มค่า, Java มีคุณลักษณะนี้ (โดยใช้finalปรับปรุง) และฉันได้มากไม่ค่อยเห็นมันใช้อื่น ๆ กว่าในกรณีที่มีการใช้ในการอนุญาตให้ตัวแปรที่จะจับโดยระดับชั้นที่ไม่ระบุชื่อ - และที่มันเป็นใช้แล้วมันทำให้ฉันรู้สึกถึงความยุ่งเหยิงมากกว่าข้อมูลที่เป็นประโยชน์


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

50
ในทางกลับกันใน F # ตัวแปรทั้งหมดเป็นแบบอ่านอย่างเดียวตามค่าเริ่มต้นและคุณต้องใช้คีย์เวิร์ด 'เปลี่ยนแปลงได้' หากคุณต้องการเปลี่ยนแปลงได้ เนื่องจาก F # เป็นภาษา. NET ฉันจึงคิดว่ามันเป็นการตรวจสอบเวลาคอมไพล์ที่คุณอธิบาย
Joel Mueller

2
@ A.Rex: คำถามคือว่าประโยชน์ของการให้คอมไพลเลอร์ทำการตรวจสอบนั้นคุ้มค่าหรือไม่เมื่ออ่านโค้ดและไม่สนใจมันจริงๆ
Jon Skeet

3
FWIW, Scala แยกแยะ local readonly/ finalvalues ​​จากตัวแปรด้วยคำหลักvalและ varในรหัส Scala valจะมีการใช้ s ท้องถิ่นบ่อยมาก (และในความเป็นจริงแล้วเป็นที่ต้องการมากกว่าvars ท้องถิ่น) ฉันสงสัยว่าสาเหตุหลักที่finalไม่ได้ใช้ตัวปรับแต่งบ่อยกว่าใน Java คือ a) ความยุ่งเหยิงและ b) ความเกียจคร้าน
Aaron Novstrup

4
สำหรับตัวแปรท้องถิ่นที่ไม่ได้ใช้ในการปิดreadonlyจะไม่มีความสำคัญมากเกินไป ในทางกลับกันสำหรับตัวแปรภายในที่ใช้ในการปิดreadonlyในหลาย ๆ กรณีจะทำให้คอมไพเลอร์สร้างโค้ดที่มีประสิทธิภาพมากขึ้น ปัจจุบันเมื่อการดำเนินการเข้าสู่บล็อกที่มีการปิดการคอมไพเลอร์จะต้องสร้างวัตถุกองใหม่สำหรับตัวแปรที่ปิดมากกว่าแม้ว่ารหัสซึ่งจะใช้การปิดไม่เคยดำเนินการ หากตัวแปรเป็นแบบอ่านอย่างเดียวโค้ดที่อยู่นอกการปิดสามารถใช้ตัวแปรปกติได้ เฉพาะเมื่อมีการสร้างตัวแทนสำหรับการปิด ...
supercat

30

ข้อเสนอแบบอ่านอย่างเดียวในท้องถิ่นและพารามิเตอร์สำหรับได้รับการพูดคุยสั้น ๆ โดยทีมออกแบบ C # 7 จากC # Design Meeting Notes วันที่ 21 มกราคม 2558 :

lambdas สามารถจับพารามิเตอร์และคนในพื้นที่ได้และเข้าถึงได้พร้อมกัน แต่ไม่มีวิธีใดที่จะปกป้องพารามิเตอร์เหล่านี้จากปัญหาที่ใช้ร่วมกันซึ่งกันและกัน: ไม่สามารถอ่านได้อย่างเดียว

โดยทั่วไปพารามิเตอร์ส่วนใหญ่และคนในท้องถิ่นจำนวนมากไม่เคยตั้งใจที่จะกำหนดให้หลังจากได้รับค่าเริ่มต้นแล้ว การอนุญาตให้อ่านอย่างเดียวจะแสดงเจตนานั้นอย่างชัดเจน

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

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

การสนทนายังคงดำเนินต่อไปใน repo การออกแบบภาษา C # โหวตเพื่อแสดงการสนับสนุนของคุณ https://github.com/dotnet/csharplang/issues/188


ยังสามารถทำให้เป็นค่าเริ่มต้นแบบอ่านอย่างเดียว (ผ่านตัวเลือกหรือคำหลักหรืออะไรก็ได้) ตัวแปรและพารามิเตอร์ส่วนใหญ่ควรเป็นแบบอ่านอย่างเดียวโดยเขียนได้เพียงเล็กน้อยเท่านั้น และการลดจำนวนที่สามารถเขียนได้โดยทั่วไปเป็นสิ่งที่ดี
Dave Cousineau

12

เป็นการกำกับดูแลสำหรับนักออกแบบภาษา c # F # มีคีย์เวิร์ด val และขึ้นอยู่กับ CLR ไม่มีเหตุผลที่ C # ไม่สามารถมีคุณสมบัติภาษาเดียวกันได้


7

ฉันเป็นเพื่อนร่วมงานคนนั้นและมันไม่เป็นมิตร! (ผมล้อเล่น)

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

โดยส่วนตัวแล้วฉันต้องการคำหลักประเภท "var" อื่นเช่น "inv" (invarient) หรือ "rvar" เพื่อหลีกเลี่ยงความยุ่งเหยิง ฉันเรียน F # มาช้าและพบว่าสิ่งที่ไม่เปลี่ยนรูปนั้นน่าสนใจ

ไม่เคยรู้ว่า Java มีสิ่งนี้


5

ฉันต้องการตัวแปรแบบอ่านอย่างเดียวในท้องถิ่นในลักษณะเดียวกับที่ฉันชอบตัวแปรconstในพื้นที่ แต่มีลำดับความสำคัญน้อยกว่าหัวข้ออื่น ๆ
บางทีลำดับความสำคัญอาจเป็นเหตุผลเดียวกับที่นักออกแบบ C # ไม่(ยัง!)ใช้คุณลักษณะนี้ แต่ควรเป็นเรื่องง่าย (และเข้ากันได้แบบย้อนหลัง) เพื่อรองรับตัวแปรแบบอ่านอย่างเดียวในเครื่องในเวอร์ชันอนาคต


2

อ่านอย่างเดียวหมายถึงสถานที่เดียวที่สามารถตั้งค่าตัวแปรอินสแตนซ์ได้คือในตัวสร้าง เมื่อประกาศตัวแปรภายในเครื่องจะไม่มีอินสแตนซ์ (อยู่ในขอบเขต) และตัวสร้างไม่สามารถสัมผัสได้


6
นั่นคือความหมายปัจจุบันของ 'อ่านอย่างเดียว' ใน C # แต่นั่นไม่ใช่คำถาม 'อ่านอย่างเดียว' มีความหมายในภาษาอังกฤษที่ดูเหมือนว่าจะมีแอปพลิเคชันที่ใช้งานง่ายสำหรับตัวแปรท้องถิ่น: คุณไม่สามารถเขียนถึงมันได้ (หลังจากเริ่มต้นแล้ว) ดูเหมือนความหมายเมื่อนำไปใช้กับตัวแปรอินสแตนซ์มากดังนั้นทำไม (ฉันคิดว่าตามเหตุผล) เราไม่สามารถใช้กับตัวแปรท้องถิ่นได้?
Spike0xff

0

ฉันรู้ว่านี่ไม่สามารถตอบคำถามของคุณได้ อย่างไรก็ตามผู้ที่อ่านคำถามนี้อาจชื่นชอบโค้ดด้านล่างอย่างไรก็ตาม

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

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

ตัวอย่างการใช้งาน:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

อาจจะไม่ใช่โค้ดที่น้อยกว่าrvar rInt = 5แต่ก็ใช้งานได้


นั่นไม่ได้ช่วยตรงนี้ ปัญหาเกี่ยวกับตัวแปร 'var' นี้คือ: {var five = 5 five = 6; Assert That (5 == 5)}
Murray

0

คุณสามารถประกาศตัวแปรโลคัลแบบอ่านอย่างเดียวใน C # หากคุณใช้คอมไพเลอร์โต้ตอบ C # csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

คุณยังสามารถประกาศตัวแปรโลคัลแบบอ่านอย่างเดียวใน.csxรูปแบบสคริปต์


5
ตามข้อความแสดงข้อผิดพลาดmessageไม่ใช่ตัวแปรที่นี่มันถูกคอมไพล์ลงในฟิลด์ นี่ไม่ใช่ nitpicking เนื่องจากความแตกต่างมีอยู่อย่างชัดเจนใน C # แบบโต้ตอบเช่นกัน: int x; Console.WriteLine(x)เป็น C # แบบโต้ตอบตามกฎหมาย (เนื่องจากxเป็นฟิลด์และเริ่มต้นโดยปริยาย) แต่void foo() { int x; Console.WriteLine(x); }ไม่ใช่ (เนื่องจากxเป็นตัวแปรและใช้ก่อนกำหนด) นอกจากนี้Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTypeจะเปิดเผยว่าxเป็นฟิลด์จริงๆไม่ใช่ตัวแปรท้องถิ่น
Jeroen Mostert

0

c # มี var แบบอ่านอย่างเดียวอยู่แล้วแม้ว่าจะมีไวยากรณ์ที่แตกต่างกันบ้าง:

พิจารณาบรรทัดต่อไปนี้:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

เปรียบเทียบกับ:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

เป็นที่ยอมรับวิธีแก้ปัญหาแรกอาจเขียนโค้ดได้น้อยกว่า แต่ส่วนย่อยที่ 2 จะทำให้อ่านอย่างเดียวชัดเจนเมื่ออ้างอิงตัวแปร


นั่นไม่ใช่ "อ่านอย่างเดียว" นั่นเป็นฟังก์ชันท้องถิ่นที่ส่งคืนตัวแปรที่ถูกจับ .. ดังนั้นค่าใช้จ่ายที่ไม่จำเป็นจำนวนมากและไวยากรณ์ที่น่าเกลียดซึ่งไม่ได้ทำให้ตัวแปรที่อยู่ภายใต้การอ่านอย่างเดียวจากมุมมองการเข้าถึงรหัส นอกจากนี้ "ไม่แน่นอน" มักจะนำไปใช้กับวัตถุที่ไม่ตัวแปรท้องถิ่น .. readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");พิจารณา:
user2864740

-2

ใช้ constคำหลักเพื่อสร้างตัวแปรแบบอ่านอย่างเดียว

อ้างอิง: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}

1
เราสนใจในรูปแบบ JavaScript constที่สามารถกำหนดตัวแปรได้ในระหว่างการเริ่มต้นเท่านั้นไม่ใช่สไตล์ csharp constที่สามารถใช้นิพจน์เวลาคอมไพล์เท่านั้น เช่นคุณทำไม่ได้const object c = new object();แต่readonlyท้องถิ่นอนุญาตให้คุณทำเช่นนี้
binki

-5

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

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