วันอังคารที่ 23 พฤศจิกายน พ.ศ. 2553

Class และ Properties

เพื่อนๆหลายคนคงรู้จักสตรัก (struct) ในภาษา C บ้างแล้ว เช่น
     struct product
     {
          char id[10];
          char name[30];
          float price;
     };

     product book, cup;

เมื่อเราทราบว่าข้อมูลจำนวนหนึ่งมีความสัมพันธ์ต่อกัน อาธิ รหัสสินค้า, ชื่อสินค้าและราคาสินค้า เราสามารถนิยามเป็นสตรักได้ กล่าวว่าเป็นการจัดกลุ่มของข้อมูลหรือนิยามชนิดข้อมูล (type) ใหม่นั่นเอง ตัวอย่างข้างต้นคือการประกาศสตรักที่มีชื่อว่า product ภายหลังได้นำสตรักนี้ไปสร้างเป็นออบเจกต์ (บางตำราเรียกว่า ตัวแปร) ชื่อ book และ cup

การนิยามคลาส
ความแตกต่างระหว่างภาษาแบบโครงสร้าง (Structural Programming) และภาษาแบบเชิงวัตถุ (Object-Oriented Programming) อยู่ที่ความสามารถในการกำหนดโครงสร้างของข้อมูลขึ้นใหม่ เพื่อใช้ในการแก้ปัญหา หากเทียบสตรักในภาษา C กับคลาสในภาษา C# คลาสยังสามารถกำหนดสภาพแวดล้อมของข้อมูลได้อีก (หลัก OOP) ซึ่งสตรักทำไม่ได้

แต่ภาษา C# สามารถนิยามสตรักแบบที่สามารถกำหนดสภาพแวดล้อมได้เช่นเดียวกับคลาส แต่เมื่อมันทั้งสองถูกสร้างเป็นออบเจกต์ หน่วยความจำจะถูกจัดการต่างกัน (จำไม่ได้ให้ย้อนกลับไปอ่านหัวข้อ สวัสดี Types)

โครงสร้างที่กำหนดสภาพแวดล้อมของข้อมูลได้ ตามนิยามของ OOP เรียกว่า "คลาส" ตัวอย่าง
     class Product
     {
          char[ ] id;
          char[ ] name;
          float price;
     }

การนิยามนี้ประกอบด้วยคำสั่ง class ตามด้วยชื่อของคลาสคือ Product และสมาชิกฟิวด์ของคลาสอีกจำนวนสามสมาชิก ได้แก่ id, name และ price

การนำคลาสไปใช้
คลาสจะถูกนำไปใช้ได้มีอยู่สองกรณีใหญ่ๆ คือ
- ถูกเรียกจากคลาสอื่น
- ถูกเรียกจาก Main ของคลาสอื่นหรือของตัวเอง
*** เพราะภาษา C# ต่างจากภาษา Java ที่มองว่า main เป็นเมธอดแบบพิเศษซึ่งเป็นสมาชิกหนึ่งของคลาส แต่ละคลาสจึงมี main เป็นของตัวเองได้ ทว่าภาษา C# ในหนึ่งงานหรือหนึ่งโปรเจกต์จะมี Main ได้เพียงเมธอดเดียว นั่นหมายความว่าจะมีเพียงคลาสเดียวเท่านั้นที่มี Main ได้

class Program
{
     static void Main(string[ ] args)
     {
          Product book = new Product();
     }
}

คลาส Program มีเมธอด Main และภายในได้สร้างออบเจกต์คลาส Product โดยมีอินสแตนท์ชื่อ book เป็นตัวรับ reference ออบเจกต์ที่เกิดขึ้น เราสามารถใช้เครื่องหมาย . (dot) ระบะต่อท้ายอินสแตนท์เพื่อเข้าถึงสมาชิกของคลาส Product ทว่าผลลัพธ์กลับเป็นดังนี้

เราไม่สามารถเข้าถึงสมาชิกฟิวด์ของคลาส Product ได้ เนื่องจากสมาชิกฟิวด์เหล่านั้น (id, name, price) ต่างถูกห่อหุ้ม (encapsulation) ไว้ ซึ่งเป็นคุณสมบัติอย่างหนึ่งของภาษาเชิงวัตถุ (OOP)

รูปแบบการนิยามคลาส

[ attributes ] [ access_modifiers ] class identifier [ : base_class ]
{
     class_body
}
attributes เราจะไม่กล่าวถึง (เพราะเป็นขั้นสูง เช่น [NoIDispatch] เป็นต้น) คนละความหมายกับแอตทริบิวต์ที่หมายถึงฟิวด์ซึ่งเป็นหนึ่งในสมาชิกของคลาส
access-modifiers หมายถึง ระดับอนุญาตการเข้าถึงสมาชิกของคลาส หากไม่ระบุจะเป็น private
identifier หมายถึง ชื่อคลาส
base_class หมายถึง ชื่อคลาสที่ต้องการสืบทอด (คลาสแม่)
class_body หมายถึง โค้ดภายในคลาส

access modifiers
การเขียนโปรแกรมเชิงวัตถุ สามารถกำหนดระดับการเข้าถึงสมาชิก (ฟิวด์, พรอเพอร์ตี้, เมธอด ฯลฯ) โดยอาจอนุญาตให้โค้ดภายในคลาสเดียวกันเท่านั้นที่เข้าถึงสมาชิกได้ หรืออาจยอมให้โค้ดภายนอกขอค่าของฟิวด์ (get) แต่ไม่ให้แก้ไขค่าของฟิวด์ (set) ทั้งนี้แล้วแต่เจตจำนงของผู้เขียนโปรแกรม การกำหนดระดับการเข้าถึงสมาชิกนี้เรียกว่า Access Modifier หรือต่อไปจะเรียกว่า ระดับอนุญาต ดังตาราง
- ที่มา msdn.microsoft.com

public หมายถึง ทุกคลาสสามารถเข้าถึงสามาชิกได้
protected หมายถึง โค้ดภายในคลาสและคลาสที่สืบทอดเท่านั้นสามารถเข้าถึงสมาชิกได้
internal หมายถึง โค้ดภายในแอสเซมบลี (assembly) เดียวกันสามารถเข้าถึงสมาชิกได้
protected internal หมายถึง โค้ดภายในคลาสและคลาสที่สืบทอดแต่ต่างแอสเซมบลี สามารถเข้าถึงสมาชิกได้
private หมายถึง โค้ดเฉพาะภายในคลาสเท่านั้นสามารถเข้าถึงสมาชิกได้

การกำหนดระดับอนุญาต
เมื่อรู้จัก access modifier แล้ว ทดลองเพิ่มโค้ดบรรทัดที่ 12 ดังนี้

จากรูปเราสามารถเข้าถึงสมาชิกฟิวด์ price ได้ เพราะมีระดับอนุญาตเป็น public แสดงว่าระดับอนุญาตก่อนขั้นตอนนี้ (default) ของทุกฟิวด์คือ private โปรดสังเกตตารางต่อไปนี้
- ที่มา msdn.microsoft.com

เช่นนั้นเมื่อต้องการเข้าถึงสมาชิกฟิวด์ทุกตัวของคลาส Product จึงควรเปลี่ยนระดับอนุญาตเป็น public ทั้งหมดใช่หรือไม่ ? คำตอบคือไม่ใช่ทั้งหมด

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

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

รูปแบบการนิยามพรอเพอร์ตี้
[ access-modifiers ] [ return_type ] identifier
{
     [ get_accessor ]
     [ set_accessor ]
}
return_type หมายถึง ไทป์ที่ถูกดำเนินการกับสมาชิกฟิวด์ ซึ่งมักเป็นไทป์เดียวกับสมาชิกฟิวด์
identifier หมายถึง ชื่อของพรอเพอร์ตี้ มักใช้ชื่อเดียวกับสมาชิกฟิวด์ เพียงแต่อักษรตัวแรกเป็นพิมพ์ใหญ่ เช่น Id, Name, Price เป็นต้น
get_accessor หมายถึง อนุญาตให้อ่าน (ขอ) ค่าสมาชิกฟิวด์ได้
set_accessor หมายถึง อนุญาตให้เขียน (กำหนด) ค่าสมาชิกฟิวด์ได้

     get accessor
     get
     {
          return expression
     }
expression หมายถึง นิพจน์คำนวณต่างๆรวมถึงชื่อสมาชิกฟิวด์ที่ต้องการอ้างถึง

     set accessor
     set
     {
          identifier = value
     }
identifier หมายถึง ชื่อสมาชิกฟิวด์ที่ต้องการอ้างถึง โดยมี value เป็นค่าใหม่ที่กำหนดมาให้
***หมายเหตุ คำสั่ง value จะมีไทป์เดียวกับ return_type เสมอ

P15_ClassAndProperties
- สร้างโปรเจกต์ใหม่เพิ่มเข้าไปยังโซลูชัน CsharpTutorialForFriends เหมือนเคย
- ตั้งชื่อมันว่า P15_ClassAndProperties
- โค้ดเพิ่มดังนี้

แบบฝึกหัด
- จงให้เหตุผลว่าเหตุใดคำสั่งภายในสี่เหลี่ยมสีส้ม ณ รูปข้างต้น จึงเกิดความผิดพลาด
- จงปรับปรุงพรอเพอร์ตี้ชื่อ Price ให้สามารถกำหนดค่าสมาชิกฟิวด์ชื่อ price ได้

7 ความคิดเห็น:

  1. Greetings from Idaho! I'm bored to death at work so I decided to check out your blog on my iphone during lunch break. I really like the info you provide here and can't wait to take a look when I get home.
    I'm surprised at how fast your blog loaded on my cell phone .. I'm not even using WIFI, just 3G .
    . Anyways, awesome blog!

    Here is my page: best cellulite treatment

    ตอบลบ
  2. ไม่ระบุชื่อ25 ตุลาคม 2558 เวลา 10:32

    ไม่รุอะ

    ตอบลบ
  3. เปลี่ยนตัว P ให้เป็นตัวเล็ก p มั่งคับ ไม่รุ้ง้ะ

    ตอบลบ
  4. ก็มันเป็น Get นิครับ

    ตอบลบ
  5. ไม่ระบุชื่อ23 พฤษภาคม 2562 เวลา 02:53

    เพราะว่าค่า get ไม่สามารถกำหนดค่าได้ ต้องไปแก้ไขที่ Class Product เป็น
    public float Price { get { return price; } set { price = value; } } จึงจะสามารถกำหนดค่าได้

    ตอบลบ