มีอะไรใหม่ใน .NET Core 3 และ C# 8 : Read only member

มีอะไรใหม่ใน .NET Core 3 และ C# 8 : Read only member
บริษัทไมโครซอฟท์ปล่อยตัวใช้งานจริงของภาษา C# version 8 แล้วในเดือนสิงหาคม 2562เมื่อต้องการใช้งาน C# version 8 ท่านจำเป็นจะต้องติดตั้ง .NET Framework version 4.8 หรือ .NET Core version 3.0 ขึ้นไปลงในคอมพิวเตอร์ของท่าน
หรือ ติดตั้งโปรแกรม Visual Studio version 16.3 ขึ้นไปที่จะทำให้ได้รับทั้ง .NET Framework version 4.8 และ .NET Core version 3.0 ไปด้วยโดยอัตโนมัติ
ใน C# version ก่อนหน้านี้จะใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้แก่ สมาชิกแบบ method ของ struct ไม่ได้
จำต้องใส่ไว้ที่ส่วนประกาศของ struct ซึ่งมีความละเอียดน้อยกว่า ดังนั้น บทความนี้จะกล่าวถึง สมาชิกแบบอ่านได้เท่านั้น
สมาชิกแบบอ่านได้เท่านั้น
ในภาษาC# version 8 เราสามารถใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้แก่สมาชิกแบบ Method ตัวใดก็ได้ ของ struct เมื่อใส่แล้วจะทำให้สมาชิกแบบ method ตัวนั้นมีภาวะอ่านได้เพียงอย่างเดียว ใน C# versionก่อนหน้านี้จะใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้แก่สมาชิกแบบ methodของ struct ไม่ได้ จำต้องใส่ไว้ที่ส่วนประกาศของ struct ซึ่งมีความละเอียดน้อยกว่า
รูปที่ 1: ตัวอย่างโค้ดแสดงวิธีใช้คำสั่ง const
ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับคำสั่ง const ทำให้สิ่งที่มันเปลี่ยนแปลงเพิ่มขยายลักษณะมีภาวะอ่านได้เพียงอย่างเดียวทั้งคู่คล้าย ๆ กัน
แต่การทำงานและวิธีใช้แตกต่างกัน การใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly เราจะใช้กับ "ตัวแปร"
ขณะที่เมื่อใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ const เราจะได้ "ตัวคงค่า" ที่มีคุณสมบัติดังนี้
ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับคำสั่ง const ทำให้สิ่งที่มันเปลี่ยนแปลงเพิ่มขยายลักษณะมีภาวะอ่านได้เพียงอย่างเดียวทั้งคู่คล้าย ๆ กัน
แต่การทำงานและวิธีใช้แตกต่างกัน การใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly เราจะใช้กับ "ตัวแปร"
ขณะที่เมื่อใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ const เราจะได้ "ตัวคงค่า" ที่มีคุณสมบัติดังนี้
- ถูกหาค่าตอน compile
- ใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ static ไม่ได้
- เปลี่ยนแปลงค่าไม่ได้
- ใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะการเข้าถึงใด ๆ ไม่ได้
- มีขอบเขตในท้องถิ่น
- จำเป็นต้องกำหนดค่าเริ่มต้นในตอนประกาศ
- บรรทัด 7,8 ฟิลด์ของคลาสที่ใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ const แล้วจะมีภาวะเป็น static ด้วย เราจะใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ static ให้มันด้วยไม่ได้ และเราจำเป็นต้องกำหนดค่าเริ่มต้นให้มันตอนประกาศ ไม่เช่นนั้นจะ compile ไม่ผ่าน
- บรรทัด 17 เราสามารถใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ const กับตัวคงค่าท้องถิ่นของ methodได้ และสามารถประกาศตัวคงค่าชนิดเดียวกันมากกว่าหนึ่งตัวในบรรทัดคำสั่งเดียวกันได้
- บรรทัด 18 การประกาศและกำหนดค่าให้แก่ตัวคงค่าจากนิพจน์สามารถทำได้ ในกรณีนี้ตัวคงค่า A จะมีค่าเป็น 11
- บรรทัด 25 เกิดเออเรอร์เพราะตอน compile เพราะค่าของ j ไม่มี จึงไม่สามารถหาค่าของนิพจน์นี้ได้
คำสั่ง readonly
รูปที่ 2: ตัวอย่างโค้ดแสดงวิธีใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly
ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly มีมาตั้งแต่ C# version แรก ต่างจากตัวคงค่าหลาย ๆ อย่าง
เช่น สิ่งที่ได้คือตัวแปรไม่ใช่ตัวคงค่า เราจะกำหนดค่าเริ่มต้นให้มันตอนประกาศ หรือ ภายหลังก็ได้ มีภาวะเป็น static ได้ มีขอบเขตเป็น globalได้ มีความเป็น public และอื่น ๆ
และแม้ว่า const จะเป็น static แต่ก็ต่างจากตัวแปรที่เป็น static
รูปที่ 2 แสดงความแตกต่างระหว่าง readonly, const และ static โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้
ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly มีมาตั้งแต่ C# version แรก ต่างจากตัวคงค่าหลาย ๆ อย่าง
เช่น สิ่งที่ได้คือตัวแปรไม่ใช่ตัวคงค่า เราจะกำหนดค่าเริ่มต้นให้มันตอนประกาศ หรือ ภายหลังก็ได้ มีภาวะเป็น static ได้ มีขอบเขตเป็น globalได้ มีความเป็น public และอื่น ๆ
และแม้ว่า const จะเป็น static แต่ก็ต่างจากตัวแปรที่เป็น static
รูปที่ 2 แสดงความแตกต่างระหว่าง readonly, const และ static โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้
- บรรทัด 9 ประกาศตัวแปร i และใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly โดยไม่กำหนดค่าเริ่มต้น
- บรรทัด 12 กำหนดค่าเริ่มต้นให้แก่ตัวแปร i ไว้ภายใน method constructor
- บรรทัด 19 ประกาศตัวแปร i และใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ static และกำหนดค่าเริ่มต้น
- บรรทัด 22 เรียกใช้งานตัวแปร i ได้โดยไม่เกิดเออเรอร์
- บรรทัด 28 ประกาศตัวแปร i ที่ไม่ใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ static และกำหนดค่าเริ่มต้น
รูปที่ 3: ตัวอย่างโค้ดการใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับสมาชิกแบบฟิลด์
รูปที่ 3 แสดงตัวอย่างโค้ดการใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับสมาชิกแบบฟิลด์ โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้
การใช้ readonly กับสมาชิกแบบฟิลด์
รูปที่ 3 แสดงตัวอย่างโค้ดการใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับสมาชิกแบบฟิลด์ โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้- บรรทัด 9 ประกาศตัวแปร i และใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly โดยไม่กำหนดค่าเริ่มต้น
- บรรทัด 12 นำค่าจากพารามิเตอร์มากำหนดให้ตัวแปรที่เป็น readonly สามารถทำได้ถ้าใส่โค้ดไว้ภายใน method constructor
- บรรทัด 16 เกิดเออเรอร์เพราะการเปลี่ยนค่าของตัวแปรที่เป็น readonly ภายใน methodอื่น ๆ ไม่สามารถทำได้
รูปที่ 4: ตัวอย่างโค้ดการกำหนดค่าเริ่มต้นให้แก่ฟิลด์ที่เป็น readonly
กำหนดค่าตัวแปร readonly
เราสามารถกำหนดค่าให้แก่ตัวคงค่าได้เฉพาะตอนประกาศ แต่ตัวแปรที่เป็นแบบ readonly เราสามารถกำหนดค่าให้มันได้หลายที่ คือ ตรงส่วนประกาศหรือภายใน method constructor แบบต่าง ๆ อย่างที่เห็นในรูปที่ 4 โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้- บรรทัด 11 แสดงการากำหนดค่าให้ตัวแปรแบบ readonly ตรงส่วนประกาศ
- บรรทัด 17 แสดงการากำหนดค่าให้ตัวแปรแบบ readonly ภายในดีฟอลท์ method constructor
- บรรทัด 22-24 แสดงการากำหนดค่าให้ตัวแปรแบบ readonly ภายใน method constructor แบบมีพารามิเตอร์ด้วยการนำค่าจากพารามิเตอร์มาใช้ในการกำหนดค่า
- บรรทัด 29 สร้าง objectและส่งค่าไปให้ method constructor เพื่อกำหนดค่าให้ตัวแปรแบบ readonly
- บรรทัด 32 กำหนดค่าให้แก่ฟิลด์ p2.x ได้อย่างไม่มีปัญหาใด ๆ
- บรรทัด 33 กำหนดค่าให้แก่ฟิลด์ p2.y ไม่ได้จึงเกิดเออเรอร์ เนื่องจากฟิลด์นี้ถูกกำหนดค่าไปแล้วในบรรทัดที่ 11 และมันเป็นตัวแปรแบบ readonly จึงกำหนดค่าซ้ำอีกครั้งไม่ได้
รูปที่ 5: ตัวอย่างโค้ด struct แบบ readonly
เมื่อใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ในนิยาม struct จะมีผลให้ struct นั้นมีภาวะเป็น immutable ซึ่งหมายความว่าเมื่อนำ struct นั้นไปสร้าง objectแล้ว
เราสามารถกำหนดค่าให้แก่ objectได้เฉพาะต้องสร้าง หลังจากนั้นจะเปลี่ยนแปลงค่าของมันไม่ได้ รูปที่ 5 แสดงตัวอย่างโค้ด struct แบบ readonly โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้
struct ที่เป็น immutable
เมื่อใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ในนิยาม struct จะมีผลให้ struct นั้นมีภาวะเป็น immutable ซึ่งหมายความว่าเมื่อนำ struct นั้นไปสร้าง objectแล้วเราสามารถกำหนดค่าให้แก่ objectได้เฉพาะต้องสร้าง หลังจากนั้นจะเปลี่ยนแปลงค่าของมันไม่ได้ รูปที่ 5 แสดงตัวอย่างโค้ด struct แบบ readonly โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้
- บรรทัด 15 ใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ที่ส่วนนิยาม struct
- บรรทัด 17, 18 AutoProperty X และ Y จะมีได้เฉพาะ getter ไม่สามารถมี setterได้ การทำเช่นนี้มีผลให้ compiler สร้าง backing field แบบอ่านได้เท่านั้น
- บรรทัด 20 ผู้ใช้สามารถกำหนดค่าให้แก่ struct ได้โดยส่งค่ามาเป็นอาร์กิวเมนต์ของมายัง method constructor ของ struct
- บรรทัด 22 overwrite method ToString เพื่อแสดงค่าของ struct
- บรรทัด 27, 28 แทนที่จะใช้การกำหนดให้ AutoProperty X และ Y จะมีแต่เกตเตอร์อย่างในบรรทัดที่ 17 และ 18 เราอาจใช้วิธีใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ที่ AutoProperty X และ Y แทนก็ได้ จะมีผลเหมือนกันคือ compiler จะสร้าง backing field แบบอ่านได้เท่านั้น
- บรรทัด 7 แสดงวิธีนำ struct มาสร้างเป็น object
- บรรทัด 8 แสดงการใช้คำสั่ง ref ในการสร้าง objectจาก struct แบบ readonly การทำเช่นนี้จะได้ค่าอ้างอิงไปยัง object และบอกให้ผู้เรียกรู้ว่าไม่สามารถเปลี่ยนแปลงค่าของ object ต้นทางได้
รูปที่ 6: ตัวอย่างโค้ดการใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ที่สมาชิกตัวใดตัวหนึ่งโดยจำเพาะเจาะจง
ในภาษา C# version 8 ที่มากับ .NET Core 3
ถ้าเราไม่ต้องการใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ที่ส่วนนิยาม struct
แต่ต้องการใส่ที่สมาชิกตัวใดตัวหนึ่งเพื่อเป็นการกำหนดโดยจำเพาะเจาะจงก็สามารถทำได้ โปรดพิจารณาโค้ดในรูปที่ 6
สมาชิก readonly
ในภาษา C# version 8 ที่มากับ .NET Core 3ถ้าเราไม่ต้องการใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ที่ส่วนนิยาม struct
แต่ต้องการใส่ที่สมาชิกตัวใดตัวหนึ่งเพื่อเป็นการกำหนดโดยจำเพาะเจาะจงก็สามารถทำได้ โปรดพิจารณาโค้ดในรูปที่ 6
- บรรทัด 13-21 เป็นนิยาม struct แบบ mutable หมายความว่าเมื่อนำ struct ไปสร้าง object เราจะสามารถเปลี่ยนแปลงค่าของ objectได้ โปรดสังเกตว่าในส่วนหัวไม่มีการใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly
- บรรทัด 15,16 สมาชิกสองตัวนี้เป็น AutoPropertyที่มีทั้ง getter และ setter ทำให้ผู้ใช้สามารถเปลี่ยนแปลงค่าของมันได้ตามใจชอบ
- บรรทัด 17 ตัวแปร Distance ที่ได้ค่าจากการนำ X และ Y มาคำนวณกับฟังก์ชัน Sqrt
- บรรทัด 19 การ overwrite method ToString ไม่ได้ทำให้เกิดการเปลี่ยนแปลงค่าใด ๆ เมื่อเรียกใช้งาน
- บรรทัด 29 แสดงการใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้กับการ overwrite method ToString เพื่อให้เห็นชัดเจนว่าไม่ได้ทำให้เกิดการเปลี่ยนแปลงค่าใด ๆ เมื่อเรียกใช้งาน การทำเช่นนี้จะเกิดข้อความเตือนตอน compile ว่า warning CS8656: Call to non-readonly member 'Point.Distance.get' from a 'readonly' member results in an implicit copy of 'this' สาเหตุที่มีข้อความเตือนตอน compileนี้เนื่องจาก method นี้เรียกใช้งานตัวแปร Distance ที่ไม่ได้เป็นแบบ readonly
- บรรทัด 37 เพื่อป้องกันไม่ให้เกิดข้อความเตือนตอน compileเราสามารถใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้กับตัวแปร Distance ได้
- บรรทัด 39 บรรทัดนี้จะไม่มีข้อความเตือนตอน compileเพราะเราใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ให้กับตัวแปร Distance ไว้ก่อนแล้ว
- บรรทัด 44, 45 สองบรรทัดนี้จะทำให้เกิด compileเออเรอร์ เพราะเราใส่ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly ไว้ที่ส่วนประกาศ method แต่โค้ดภายใน methodมีคำสั่งเปลี่ยนแปลงค่าของตัวแปร X และ Y แม้ว่าตัวแปรสองตัวนี้จะไม่ได้เป็น readonly ก็ตาม
รูปที่ 7: ตัวอย่างโค้ด struct ที่เป็น immutable

รูปที่ 8: ผลลัพธ์การทำงานของตัวอย่างโค้ด struct ที่เป็น immutable
ในหัวข้อที่ผ่านมาเราใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับ struct มีผลให้มันมีภาวะเป็น immutable แต่ struct ในภาษา C# มีภาวะเป็น immutable อยู่แล้ว
ในหัวข้อนี้จะอธิบายความหมายของ mutable และ immutable และแสดงโค้ดเพื่อให้เห็นว่าการที่ struct จะมีภาวะเป็น mutable มีผลอย่างไรต่อการทำงานของโปรแกรม
type ที่เป็น immutable คือ primitive type เช่น int, double, char, string ฯลฯ
รวมถึง type ที่ผู้ใช้กำหนดขึ้นเอง (user defined type) ได้แก่ class และ struct type เหล่านี้ถือว่าเป็น value type
เพราะเมื่อเราส่งมันไปยังฟังก์ชันใด ๆ จะเกิดการคัดลอกทำสำเนาค่าส่งไป
เมื่อเปลี่ยนแปลงค่าของมันภายในฟังก์ชันจะไม่ส่งผลต่อตัวจริงที่อยู่นอกฟังก์ชัน
type ที่เป็น mutable คือ type พวก Array, List, generic type อย่าง Stack, Queue ฯลฯ
type เหล่านี้ถือว่าเป็น reference type เพราะเมื่อเราผ่านมันไปยังฟังก์ชันใด ๆ จะไม่เกิดการคัดลอกทำสำเนาค่าส่งไป
แต่จะส่งค่าอ้างอิงของ object มีผลให้เมื่อเปลี่ยนแปลงค่าของมันภายในฟังก์ชัน จะส่งผลต่อตัวจริงที่อยู่นอกฟังก์ชันด้วย
รูปที่ 7 แสดงตัวอย่างโค้ด struct ที่เป็น immutable
โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้ และดูภาพผลลัพธ์การทำงานของโปรแกรมในรูปที่ 8 ประกอบด้วย
บทความนี้เป็นตอนแรกที่ผมได้พูดถึงคุณสมบัติที่มาใหม่ในภาษาC# 8 และ .NET Core 3 ที่เกี่ยวข้องกับ
ในบทความตอนหน้าจะพูดถึงคุณสมบัติที่น่าสนใจใหม่ ๆ ของC#และ .NET Core version ใหม่ล่าสุด
struct ที่ไม่ใส่ readonly
ในหัวข้อที่ผ่านมาเราใช้ตัวเปลี่ยนแปลงเพิ่มขยายลักษณะ readonly กับ struct มีผลให้มันมีภาวะเป็น immutable แต่ struct ในภาษา C# มีภาวะเป็น immutable อยู่แล้วในหัวข้อนี้จะอธิบายความหมายของ mutable และ immutable และแสดงโค้ดเพื่อให้เห็นว่าการที่ struct จะมีภาวะเป็น mutable มีผลอย่างไรต่อการทำงานของโปรแกรม
type ที่เป็น immutable คือ primitive type เช่น int, double, char, string ฯลฯ
รวมถึง type ที่ผู้ใช้กำหนดขึ้นเอง (user defined type) ได้แก่ class และ struct type เหล่านี้ถือว่าเป็น value type
เพราะเมื่อเราส่งมันไปยังฟังก์ชันใด ๆ จะเกิดการคัดลอกทำสำเนาค่าส่งไป
เมื่อเปลี่ยนแปลงค่าของมันภายในฟังก์ชันจะไม่ส่งผลต่อตัวจริงที่อยู่นอกฟังก์ชัน
type ที่เป็น mutable คือ type พวก Array, List, generic type อย่าง Stack, Queue ฯลฯ
type เหล่านี้ถือว่าเป็น reference type เพราะเมื่อเราผ่านมันไปยังฟังก์ชันใด ๆ จะไม่เกิดการคัดลอกทำสำเนาค่าส่งไป
แต่จะส่งค่าอ้างอิงของ object มีผลให้เมื่อเปลี่ยนแปลงค่าของมันภายในฟังก์ชัน จะส่งผลต่อตัวจริงที่อยู่นอกฟังก์ชันด้วย
รูปที่ 7 แสดงตัวอย่างโค้ด struct ที่เป็น immutable
โปรดสังเกตสิ่งต่าง ๆ ดังต่อไปนี้ และดูภาพผลลัพธ์การทำงานของโปรแกรมในรูปที่ 8 ประกอบด้วย
- บรรทัด 26-30 สร้าง object myP1 หนึ่งตัวจาก struct ได้ผลลัพธ์อย่างบรรทัดที่ 4 รูปที่ 8
- บรรทัด 32-37 สร้าง object myP1a จาก struct เดิม ได้ผลลัพธ์อย่างบรรทัดที่ 6 และ 7 ในรูปที่ 8 โปรดสังเกตว่า object 2 ตัวนี้มีค่าต่างกัน
- บรรทัด 39-45 สร้าง object myP1b จาก myP1 แล้วเปลี่ยนแปลงค่าของ myP1b ได้ผลลัพธ์อย่างบรรทัดที่ 9 ถึง 11 ในรูปที่ 8 จะเห็นว่าค่าของ myP1 ไม่เปลี่ยนแปลง
- บรรทัด 47-49 ส่ง myP1 เป็นอาร์กิวเมนต์ของ method มีการเปลี่ยนแปลงค่าใน method ได้ผลลัพธ์อย่างบรรทัดที่ 9 ถึง 11 ในรูปที่ 8 จะเห็นว่าค่าของ myP1 ไม่เปลี่ยนแปลง
บทความนี้เป็นตอนแรกที่ผมได้พูดถึงคุณสมบัติที่มาใหม่ในภาษาC# 8 และ .NET Core 3 ที่เกี่ยวข้องกับ
- สมาชิกแบบอ่านได้เท่านั้น
- คำสั่ง readonly การใช้ readonly กับ สมาชิกแบบฟิลด์
- การกำหนดค่าตัวแปร readonly struct ที่เป็น immutable
- สมาชิก readonly struct ที่ไม่ใส่ readonly
ในบทความตอนหน้าจะพูดถึงคุณสมบัติที่น่าสนใจใหม่ ๆ ของC#และ .NET Core version ใหม่ล่าสุด