F# มี syntax พิเศษที่ช่วยให้เขียน Pattern matching ได้ง่ายขึ้น โดยใช้โครงสร้างคล้าย ๆ กับ Union ใช้วิธีประกาศ Test case ที่ต้องการในเครื่องหมาย (||) Active pattern มีหลายแบบ ขึ้นอยู่กับจำนวน Case ที่ใช้
Single-Case Active Patterns
เป็น Active pattern ที่มี Case เดียวและมี Input เพียงตัวเดียว Active pattern แบบนี้ต้อง Return ผลลัพทธ์เสมอ
1 2 3 4 5 6 7
let (|Remainder2|) x = x % 2 let checkNumber = function | Remainder2 1 -> "even number" | Remainder2 0 -> "odd number"
// "even number" checkNumber 1
จากตัวอย่าง Remainder2 เป็น Pattern ที่มี Case โดยคำนวณค่า Mod จาก Input ที่ส่งเข้ามา
การใช้งาน generic ถ้าระบุ type argument ครบจำนวนที่ประกาศไว้ในเครื่องหมาย <> เราจะเรียก type นั้นว่า closed type ถ้ามีการระบุไว้เพียงบางส่วน จะเรียกว่า structured type แต่ถ้าไม่ระบุ type argument เลยจะเรียกว่า open type
ความแตกต่างระหว่าง open และ closed type
Open type ถือว่าเป็น type ที่ไม่สมบูรณ์ จึงไม่สามารถสร้าง instance ได้ แต่สามารถใช้เป็น input ของ operator typeof
Close type สามารถสร้าง instance ได้เหมือนคลาสทั่วไป
ตัวอย่าง
สร้าง GenericStruct เป็น generic type ง่าย ๆ โดยมีการระบุเงื่อนไขว่า type argument ต้องเป็น value type เท่านั้น
1 2 3 4 5 6 7 8 9 10 11
using System; using System.Runtime.InteropServices; using static System.Console;
public class GenericStruct<T> where T: struct { public string Name { get { return typeof(T).Name; } } }
ทดสอบ
ทดสอบว่าสามารถสร้าง instance ของ open และ closed type ผ่าน Activator.CreateInstance ได้หรือไม่
Primitive type ใน C# เช่น short float หรือ int จะมี min และ max value เป็นค่าต่ำสุดและสูงสุดที่เป็นไปได้ ค่านี้ถูกกำหนดโดยจำนวน bit ของ type เช่น int มี 32 bit เลขฐานสองขนาด 32 bit สามารถแสดงตัวเลขได้ถึง 4294967296 แต่เนื่องจาก int เป็น sign integer ต้องใช้ 1 bit สำหรับเก็บ flag + หรือ - ดังนั้น bit ที่ใช้สำหรับเก็บตัวเลขจึงเหลือ 31 bit ทำให้สามารถเก็บค่าต่ำสุดและสูงสุดดังนี้
Unhandled Exception: System.OverflowException: Arithmetic operation resulted in an overflow. at Test.Main (System.String[] args) in <filename unknown>:line 0 [ERROR] FATAL UNHANDLED EXCEPTION: System.OverflowException: Arithmetic operation resulted in an overflow. at Test.Main (System.String[] args) in <filename unknown>:line 0
สรุป
จากการทดสอบจะเห็นว่า โปรแกรมที่ไม่เช็ค overflow สามารถทำงานได้ โดยไม่มี error แต่ผลลัพท์ไม่ถูกต้อง ในการใช้งานจริงโปรแกรมเมอร์ต้องแน่ใจว่า โค้ดที่เขียนจะต้องอยู่ภายใน range ของ type นั้น มิฉะนั้นโปรแกรมก็จะทำงานผิดพลาด
using System; publicclassTest { publicvoidChecked1() { Int32 i = Int32.MaxValue; i = checked (i + 1); } publicvoidChecked2() { checked { Int32 i = Int32.MaxValue; i = i + 1; } } publicvoidUnchecked() { unchecked { Int32 i = Int32.MaxValue; i = i + 1; } } }
ใน C# จะมี alias ที่ match กับ type ใน base class library (BCL) เป็น keyword ตัวพิมพ์เล็ก เช่น System.String มี alias คือ string ทั้ง String ใหญ่และ string เล็กในทางเทคนิกแล้วสามารถใช้แทนกันได้
Alias ของ C# มีทั้งหมด 15 ตัว โดย map อยู่กับ BCL type ดังนี้
abstractasbaseboolbreakbytecasecatchcharchecked class const continuedecimaldefaultdelegatedodoubleelseenumeventexplicit externfalsefinallyfixedfloatforforeachgotoifimplicitin in (generic modifier) intinterfaceinternalislocklong namespace new nullobjectoperatoroutout (generic modifier) overrideparams privateprotectedpublicreadonlyref return sbytesealedshortsizeof stackallocstaticstringstructswitchthis throw truetrytypeofuint ulonguncheckedunsafeushortusingvirtualvoidvolatilewhile
เมื่อ alias กับ BCL type ทำงานเหมือนกัน แล้วจะเลือกใช้ตัวไหน? เรื่องนี้มีความเห็นหลายแบบ เช่น
ใน C# coding style ของทีม corefx บอกว่าพวกเขาใช้ alias ทั้งหมดทุกกรณี เหตุผลอย่างหนึ่งคือ alias เป็น keyword ประโยชน์ที่ได้จากมันก็คือ สามารถเรียกใช้เมื่อไหร่ก็ได้ โดยไม่ต้องเปิด namespace ใด ๆ แม้ได้ System
บางคนให้ความเห็นว่า engineer ของ Microsoft สร้าง alias เพื่อให้คล้ายกับ C++ เพื่อให้โปรแกรมเมอร์เข้าใจได้ง่าย เป็นแนวคิดเก่า ตอนนี้ก็ควรกลับมาใช้ BCL type ซึ่งเป็น design เริ่มต้นของ C# เสียที [1]
ทำไมถึงควรใช้ BCL type ก็เนื่องจาก Api บางตัวของ .Net ใช้ชื่อเดียวกับ BCL type เช่น Convert.ToSingle ฟังก์ชันนี้ return ค่าเป็น Single (float) สามารถเขียนได้สองแบบ คือ
1 2
floatvalue = Convert.ToSingle("12.0") Single value = Convert.ToSingle("12.0")
แบบแรก ต้องการ convert เป็น Single แต่กลับ return ค่าออกมาเป็น float แบบที่สอง จะดูตรงไปตรงมา คือ convert เป็น Single และรับค่าด้วย Single
ความเห็นอีกแบบคือ ควรใช้ alias เป็นหลัก จะใช้ BCL type ก็ต่อเมื่อมีการอ้างถึงสมาชิกในคลาสเท่านั้น เพราะไม่ make sense ถ้าจะเรียกใช้ method จาก keyword ควรเรียกจาก class มากกว่า