มีการถกเถียงอย่างเป็นกันเองกับเพื่อนร่วมงานเกี่ยวกับเรื่องนี้ เรามีความคิดบางอย่างเกี่ยวกับเรื่องนี้ แต่สงสัยว่ากลุ่ม SO คิดอย่างไรกับเรื่องนี้?
มีการถกเถียงอย่างเป็นกันเองกับเพื่อนร่วมงานเกี่ยวกับเรื่องนี้ เรามีความคิดบางอย่างเกี่ยวกับเรื่องนี้ แต่สงสัยว่ากลุ่ม SO คิดอย่างไรกับเรื่องนี้?
คำตอบ:
เหตุผลหนึ่งคือไม่มีการรองรับ CLR สำหรับโลคัลแบบอ่านอย่างเดียว อ่านอย่างเดียวถูกแปลเป็นรหัส opcode เริ่มต้นของ CLR / CLI แฟล็กนี้สามารถใช้ได้กับฟิลด์เท่านั้นและไม่มีความหมายสำหรับโลคัล ในความเป็นจริงการนำไปใช้กับท้องถิ่นอาจทำให้เกิดรหัสที่ไม่สามารถตรวจสอบได้
นี่ไม่ได้หมายความว่า C # ไม่สามารถทำได้ แต่มันจะให้สองความหมายที่แตกต่างกันสำหรับโครงสร้างภาษาเดียวกัน เวอร์ชันสำหรับชาวบ้านจะไม่มีการแมปที่เทียบเท่ากับ CLR
readonly
คำหลักสำหรับเขตข้อมูลความต้องการที่จะได้รับการสนับสนุนโดย CLI เพราะผลของมันคือสามารถมองเห็นได้ประกอบอื่น ๆ ทั้งหมดนี้หมายความว่าตัวแปรมีการกำหนดเพียงครั้งเดียวในเมธอดในเวลาคอมไพล์
const
(ซึ่งใน C ++ คล้าย C # readonly
มากกว่าเหมือน C # const
แม้ว่าจะสามารถเล่นได้ทั้งสองบทบาทก็ตาม) ยังรองรับ C ++ const
สำหรับตัวแปรอัตโนมัติในเครื่อง ดังนั้นการขาดการสนับสนุน CLR สำหรับ C # readonly
สำหรับตัวแปรโลคัลจึงไม่เกี่ยวข้อง
using
และout
กำลังทำอย่างนั้นและโลกก็ไม่ได้พังทลาย
ฉันคิดว่ามันเป็นการตัดสินที่ไม่ดีในส่วนของสถาปนิก C # ตัวแก้ไขแบบอ่านอย่างเดียวบนตัวแปรในระบบช่วยรักษาความถูกต้องของโปรแกรม (เช่นเดียวกับการยืนยัน) และอาจช่วยให้คอมไพเลอร์ปรับแต่งโค้ดได้ดีที่สุด (อย่างน้อยก็ในกรณีของภาษาอื่น ๆ ) ความจริงที่ว่ามันไม่ได้รับอนุญาตใน C # ในขณะนี้เป็นอีกหนึ่งข้อโต้แย้งที่ว่า "คุณสมบัติ" บางอย่างของ C # เป็นเพียงการบังคับใช้รูปแบบการเข้ารหัสส่วนบุคคลของผู้สร้างเท่านั้น
ในการตอบคำตอบของ Jared มันอาจจะต้องเป็นคุณสมบัติเวลาคอมไพล์ - คอมไพเลอร์จะห้ามไม่ให้คุณเขียนถึงตัวแปรหลังจากการประกาศครั้งแรก (ซึ่งจะต้องรวมการมอบหมาย)
ฉันสามารถเห็นคุณค่าในสิ่งนี้ได้หรือไม่? เป็นไปได้ - แต่ไม่มากนักที่จะพูดตามตรง หากคุณไม่สามารถบอกได้อย่างง่ายดายว่าจะมีการกำหนดตัวแปรที่อื่นในเมธอดหรือไม่แสดงว่าเมธอดของคุณยาวเกินไป
สำหรับสิ่งที่คุ้มค่า, Java มีคุณลักษณะนี้ (โดยใช้final
ปรับปรุง) และฉันได้มากไม่ค่อยเห็นมันใช้อื่น ๆ กว่าในกรณีที่มีการใช้ในการอนุญาตให้ตัวแปรที่จะจับโดยระดับชั้นที่ไม่ระบุชื่อ - และที่มันเป็นใช้แล้วมันทำให้ฉันรู้สึกถึงความยุ่งเหยิงมากกว่าข้อมูลที่เป็นประโยชน์
readonly
/ final
values จากตัวแปรด้วยคำหลักval
และ var
ในรหัส Scala val
จะมีการใช้ s ท้องถิ่นบ่อยมาก (และในความเป็นจริงแล้วเป็นที่ต้องการมากกว่าvar
s ท้องถิ่น) ฉันสงสัยว่าสาเหตุหลักที่final
ไม่ได้ใช้ตัวปรับแต่งบ่อยกว่าใน Java คือ a) ความยุ่งเหยิงและ b) ความเกียจคร้าน
readonly
จะไม่มีความสำคัญมากเกินไป ในทางกลับกันสำหรับตัวแปรภายในที่ใช้ในการปิดreadonly
ในหลาย ๆ กรณีจะทำให้คอมไพเลอร์สร้างโค้ดที่มีประสิทธิภาพมากขึ้น ปัจจุบันเมื่อการดำเนินการเข้าสู่บล็อกที่มีการปิดการคอมไพเลอร์จะต้องสร้างวัตถุกองใหม่สำหรับตัวแปรที่ปิดมากกว่าแม้ว่ารหัสซึ่งจะใช้การปิดไม่เคยดำเนินการ หากตัวแปรเป็นแบบอ่านอย่างเดียวโค้ดที่อยู่นอกการปิดสามารถใช้ตัวแปรปกติได้ เฉพาะเมื่อมีการสร้างตัวแทนสำหรับการปิด ...
ข้อเสนอแบบอ่านอย่างเดียวในท้องถิ่นและพารามิเตอร์สำหรับได้รับการพูดคุยสั้น ๆ โดยทีมออกแบบ C # 7 จากC # Design Meeting Notes วันที่ 21 มกราคม 2558 :
lambdas สามารถจับพารามิเตอร์และคนในพื้นที่ได้และเข้าถึงได้พร้อมกัน แต่ไม่มีวิธีใดที่จะปกป้องพารามิเตอร์เหล่านี้จากปัญหาที่ใช้ร่วมกันซึ่งกันและกัน: ไม่สามารถอ่านได้อย่างเดียว
โดยทั่วไปพารามิเตอร์ส่วนใหญ่และคนในท้องถิ่นจำนวนมากไม่เคยตั้งใจที่จะกำหนดให้หลังจากได้รับค่าเริ่มต้นแล้ว การอนุญาตให้อ่านอย่างเดียวจะแสดงเจตนานั้นอย่างชัดเจน
ปัญหาหนึ่งคือคุณลักษณะนี้อาจเป็น "สิ่งที่น่ารำคาญ" ในขณะที่ "สิ่งที่ถูกต้อง" ที่ต้องทำเกือบตลอดเวลาคือการทำให้พารามิเตอร์และภาษาท้องถิ่นอ่านอย่างเดียวมันจะทำให้โค้ดยุ่งเหยิงอย่างมากในการทำเช่นนั้น
ความคิดในการบรรเทาบางส่วนคือการอนุญาตให้ชุดค่าผสมแบบอ่านอย่างเดียวของตัวแปรท้องถิ่นถูกทำสัญญากับ val หรืออะไรทำนองนั้น โดยทั่วไปแล้วเราสามารถลองนึกถึงคำหลักที่สั้นกว่าคำหลักที่สร้างขึ้นแบบอ่านอย่างเดียวเพื่อแสดงความเป็นแบบอ่านอย่างเดียว
การสนทนายังคงดำเนินต่อไปใน repo การออกแบบภาษา C # โหวตเพื่อแสดงการสนับสนุนของคุณ https://github.com/dotnet/csharplang/issues/188
เป็นการกำกับดูแลสำหรับนักออกแบบภาษา c # F # มีคีย์เวิร์ด val และขึ้นอยู่กับ CLR ไม่มีเหตุผลที่ C # ไม่สามารถมีคุณสมบัติภาษาเดียวกันได้
ฉันเป็นเพื่อนร่วมงานคนนั้นและมันไม่เป็นมิตร! (ผมล้อเล่น)
ฉันจะไม่กำจัดฟีเจอร์นี้เพราะควรเขียนวิธีการสั้น ๆ ดีกว่า มันเหมือนกับการบอกว่าคุณไม่ควรใช้เธรดเพราะมันยาก เอามีดมาให้ฉันและให้ฉันรับผิดชอบที่ไม่ได้ตัดเอง
โดยส่วนตัวแล้วฉันต้องการคำหลักประเภท "var" อื่นเช่น "inv" (invarient) หรือ "rvar" เพื่อหลีกเลี่ยงความยุ่งเหยิง ฉันเรียน F # มาช้าและพบว่าสิ่งที่ไม่เปลี่ยนรูปนั้นน่าสนใจ
ไม่เคยรู้ว่า Java มีสิ่งนี้
ฉันต้องการตัวแปรแบบอ่านอย่างเดียวในท้องถิ่นในลักษณะเดียวกับที่ฉันชอบตัวแปรconstในพื้นที่ แต่มีลำดับความสำคัญน้อยกว่าหัวข้ออื่น ๆ
บางทีลำดับความสำคัญอาจเป็นเหตุผลเดียวกับที่นักออกแบบ C # ไม่(ยัง!)ใช้คุณลักษณะนี้ แต่ควรเป็นเรื่องง่าย (และเข้ากันได้แบบย้อนหลัง) เพื่อรองรับตัวแปรแบบอ่านอย่างเดียวในเครื่องในเวอร์ชันอนาคต
อ่านอย่างเดียวหมายถึงสถานที่เดียวที่สามารถตั้งค่าตัวแปรอินสแตนซ์ได้คือในตัวสร้าง เมื่อประกาศตัวแปรภายในเครื่องจะไม่มีอินสแตนซ์ (อยู่ในขอบเขต) และตัวสร้างไม่สามารถสัมผัสได้
ฉันรู้ว่านี่ไม่สามารถตอบคำถามของคุณได้ อย่างไรก็ตามผู้ที่อ่านคำถามนี้อาจชื่นชอบโค้ดด้านล่างอย่างไรก็ตาม
หากคุณกังวลกับการถ่ายภาพตัวเองด้วยเท้าเมื่อลบล้างตัวแปรท้องถิ่นที่ควรตั้งค่าเพียงครั้งเดียวและคุณไม่ต้องการให้เป็นตัวแปรที่เข้าถึงได้ทั่วโลกมากขึ้นคุณสามารถทำสิ่งนี้ได้
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
แต่ก็ใช้งานได้
คุณสามารถประกาศตัวแปรโลคัลแบบอ่านอย่างเดียวใน 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
รูปแบบสคริปต์
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
เป็นฟิลด์จริงๆไม่ใช่ตัวแปรท้องถิ่น
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!");
พิจารณา:
ใช้ 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);
}
}
const
ที่สามารถกำหนดตัวแปรได้ในระหว่างการเริ่มต้นเท่านั้นไม่ใช่สไตล์ csharp const
ที่สามารถใช้นิพจน์เวลาคอมไพล์เท่านั้น เช่นคุณทำไม่ได้const object c = new object();
แต่readonly
ท้องถิ่นอนุญาตให้คุณทำเช่นนี้
ฉันคิดว่านั่นเป็นเพราะฟังก์ชันที่มีตัวแปรแบบอ่านอย่างเดียวอาจไม่ถูกเรียกและอาจมีบางอย่างเกี่ยวกับมันที่อยู่นอกขอบเขตและคุณจะต้องทำเมื่อใด