Monthly Archives: ธันวาคม 2006

ตอน 22 method parameter

ไปหน้าแรก      สารบัญ        Laploy.com    ระเบียนบทความ      บทความจากลาภลอย


เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาในBlog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

method parameter

ภาษา C# ก็เช่นเดียวกับภาษาอื่นๆ ที่เมื่อเรานิยาม method เราสามารถกำหนดให้มีพารามิเตอร์หรือไม่ก็ได้ พารามิเตอร์คือการส่งค่าให้ method พารามิเตอร์ในภาษา C# มี syntax ดังนี้

[modifier] data type ชื่อพารามิเตอร์

    • modifier ทำหน้าที่กำหนดวิธีส่ง (ดูย่อหน้าต่อไป)
    • data type คือลักษณะของข้อมูล เช่น int, byte, bool
    • ชื่อพารามิเตอร์ เป็นชื่อที่เราตั้งให้แก่พารามิเตอร์ เช่น salary, studentName

พารามิเตอร์ในภาษา C# มีสี่แบบคือ
    1. value parameter: ไม่ต้องใส่ modifier
    2. ref parameter: ใส่ modifier ref
    3. out parameter: ใส่ modifier out
    4. params parameter: ใส่ modifier params

Value parameter: บางคนเรียก in parameter เป็นวิธีปรกติธรรมดา หากท่านไม่ระบุ CLR จะถือว่าเป็นการส่งพารามิเตอร์แบบนี้โดยปริยาย การส่งแบบนี้ CLR จะสร้างตัวแปรขึ้นใหม่ภายใน method ที่รับพารามิเตอร์ และจะกำหนดค่าเริ่มต้นของมันเป็นข้อมูลที่ท่านส่งไป หากท่านเปลี่ยนแปลงตัวแปรนี้ ข้อมูลดั้งเดิมของท่านจะไม่ได้รับผลกระทบ โปรดดูตัวอย่างโค้ดต่อไปนี้

1     using System;
2
3     namespace ConsoleApplication1
4     {
5         class Program
6         {
7             static void Main(string[] args)
8             {
9                 int i = 2, j = 3;
10                Console.WriteLine(Add(i, j));
11                Console.WriteLine(i);
12                Console.WriteLine(j);
13                Console.ReadLine();
14            }
15            static int Add(int i, int j)
16            {
17                i++;
18                j++;
19                return i + j;
20            }
21        }
22     } 

บรรทัดที่ 9 มีการสร้างตัวแปรชื่อ i และ j จากนั้นส่งค่าของ i และ j ไปเป็นพารามิเตอร์ให้แก่ method Add ในบรรทัดที่ 10 โดยไม่ระบุ modifier ซึ่งจะทำให้เป็น value parameter ไปโดยปริยาย จากนั้นภายใน method Add บรรทัดที่ 17,18 มีการเปลี่ยนแปลงค่า i และ j แต่ตัวแปร i และ j ที่อยู่ภายใน method Main จะไม่ได้รับผลกระทบ (ไม่เปลี่ยนแปลง) ผลลัพธ์ของโปรแกรมนี้คือ

7
2
3

ref parameter: การส่งพารามิเตอร์แบบนี้ไม่ได้ส่งค่าของตัวแปร แต่เป็นการส่งตัวแปรไปจริงๆ จึงไม่มีการสร้างตัวแปรใหม่ แต่จะใช้ตัวแปรนั้นๆ โดยตรง เมื่อเราเปลี่ยนแปลงค่าของตัวแปรภายใน method ที่รับพารามิเตอร์ ค่าของตัวแปรใน method ที่ส่งพารามิเตอร์จะเปลี่ยนไปด้วย โปรดดูตัวอย่างต่อไปนี้

1     using System;
2 
3     namespace ConsoleApplication1
4     {
5         class Program
6         {
7             static void Main(string[] args)
8             {
9                  int i = 2, j = 3;
10                 Console.WriteLine(Add(ref i, ref j));
11                 Console.WriteLine(i);
12                 Console.WriteLine(j);
13                 Console.ReadLine();
14            }
15            static int Add(ref int i, ref int j)
16            {
17                i++;
18                j++;
19                return i + j;
20            }
21        }
22     } 

เมื่อรันโปรแกรมนี้ค่าที่ได้จะเป็น

7
3
4

โปรดสังเกตด้วยว่า modifier ref จะต้องใส่ไว้ทั้งที่โค้ดซึ่งเป็นตัวส่งพารามิเตอร์ (บรรทัดที่ 10) และโค้ดส่วนที่รับพารามิเตอร์ (บรรทัดที่ 15)

out parameter: การส่งพารามิเตอร์แบบนี้คล้ายๆ ref ในแง่ที่เป็นการส่งตัวแปรไปจริงๆ จึงไม่มีการสร้างตัวแปรใหม่ แต่จะแตกต่างจากการส่งแบบ ref ตรงที่ตัวแปรที่ถูกส่งไปไม่จำเป็นต้องถูกกำหนดค่าไว้ก่อนก็ได้ โปรดดูโค้ดต่อไปนี้

1     using System;
2
3     namespace ConsoleApplication1
4     {
5         class Program
6         {
7             static void Main(string[] args)
8             {
9                  int i, j;
10                 Console.WriteLine(Add(out i, out j));
11                 Console.WriteLine(i);
12                 Console.WriteLine(j);
13                 Console.ReadLine();
14             }
15             static int Add(out int i, out int j)
16             {
17                 i=2;
18                 j=3;
19                 return i + j;
20             }
21         }
22     } 

โปรดสังเกตว่าเราสร้างตัวแปร i และ j ไว้ใน method Main ในบรรทัดที่ 9 โดยไม่ได้กำหนดค่าให้มัน แล้วเราส่งมันไปเป็นพารามิเตอร์ทันทีในบรรทัดที่ 10 เมื่อรันโปรแกรมนี้ค่าที่ได้จะเป็น

5
2
3

เช่นเดียวกับ modifier ref modifier out จะต้องถูกใส่ไว้ทั้งที่โค้ดซึ่งเป็นตัวส่งพารามิเตอร์ (บรรทัดที่ 10) และโค้ดส่วนที่รับพารามิเตอร์ (บรรทัดที่ 15)

params parameter: ใช้สำหรับการส่ง array หรือชุดของข้อมูลไปเป็นพารามิเตอร์ จึงเรียกอีกชื่อหนึ่งว่า parameter array (PA) PA เป็นสิ่งที่มีประโยชน์มากเพราะช่วยให้เราสามารถส่งข้อมูลเป็นชุดให้แก่ method ได้โดยไม่ต้องเขียนรายการพารามิเตอร์ (parameter list) ยืดยาว โปรดดูตัวอย่างโค้ดต่อไปนี้

1     using System;
2 
3     namespace ConsoleApplication1
4     {
5         class Program
6         {
7             static void Main(string[] args)
8             {
9                  int[] x = { 1, 2, 3 };
10                 ShowNumbers(x);
11                 ShowNumbers(4, 5);
12                 Console.ReadLine();
13            }
14            static void ShowNumbers(params int[] numbers)
15            {
16                 foreach (int x in numbers)
17                 {
18                      Console.Write(x + " ");
19                 }
20                 Console.WriteLine();
21            }
22        }
23    } 

โค้ดใน method Main สร้าง array (บรรทัดที่ 9) และส่งไปเป็นพารามิเตอร์ให้กับ method ShowNumbers ในบรรทัดที่ 10 และ 11 เมื่อรันโปรแกรมนี้ผลลัพธ์ที่จะเป็น

1 2 3
4 5

โปรดสังเกตว่าการใส่ modifier params ให้ใส่เฉพาะที่ method ที่เป็นตัวรับพารามิเตอร์เท่านั้น (ตัวที่ส่งไม่ต้องใส่ keyword ใดๆ) ในกรณีที่ใช้ PA ร่วมกับพารามิเตอร์อื่นๆ PA จะต้องเป็นตัวสุดท้าย (ตัวขวาสุด) เสมอ และ PA ต้องเป็น array มิติเดียวเท่านั้น

ภาษา C# ใช้ PA เป็นเรื่องปรกติ แม้แต่ method Main ก็ใช้ PA ไลบรารี .NET มีการใช้ PA อยู่ทั่วไป ยกตัวอย่างเช่น method Console.WriteLine() ก็ใช้ PA ทำให้เราเขียนโค้ดแบบนี้ได้

Console.WriteLine(“x={0} y ={1} z={2}”, x, y, z);

จะเห็นว่าการเรียกใช้ method WriteLine() เราสามารถใส่ argument กี่ตัวก็ได้ตามใจชอบ

 

พารามิเตอร์กับ argument

คำว่าพารามิเตอร์มักถูกใช้ปะปนกันคำว่า argument (อาร์กิวเมนท์) เพราะมีความหมายคล้ายๆ กัน พารามิเตอร์หมายถึงรายการตัวแปรไว้รับค่า ที่เรานิยามไว้ในวงเล็บหลังชื่อของ method ส่วน argument หมายถึงรายการค่า หรือตัวแปรที่เราส่งให้กับ method เมื่อเรียกใช้ method

ยกตัวอย่างเช่นในโค้ดข้างบน บรรทัดที่ 14 สิ่งที่อยู่ภายในวงเล็บคือพารามิเตอร์ ส่วนบรรทัดที่ 10 สิ่งที่อยู่ภายในวงเล็บคือ argument ในกรณีที่ส่งค่าเป็น argument มากกว่าหนึ่งค่า ยกตัวอย่างบรรทัดที่ 11 จะเรียกว่า argument list

 

เดนนิส ริชชี มีคุณูปการแก่วงการคอมพิวเตอร์ เพราะเป็นผู้คิดประดิษฐ์ภาษา ซี อันเป็นภาษาที่มีอิทธิผลต่อภาษาในยุคต่อมา อาธิ ภาษา C++, JavaScript, Java และ C#

 

method signature

method signature คือรายการพารามิเตอร์ของ method รายการพารามิเตอร์คือค่าต่างๆ ที่เราส่งให้กับ method ลองพิจารณาเมธอดนี้


1 public int AddNumber(int x, int y)
2 {
3     answer = x + y;
4     return answer;
5 } 

ในตัวอย่างนี้ AddNumber เป็น method ที่มี return type เป็นแบบ int และต้องการรับพารามิเตอร์สองตัวที่เป็น int ทั้งคู่ คือ x และ y ในกรณีนี้ x และ y คือรายการพารามิเตอร์

ลำดับ จำนวน และ type ของพารามิเตอร์ที่เราส่งให้เมธอดจะต้องตรงกับพารามิเตอร์ list ที่นิยามไว้ตรงหัว method มิฉะนั้นจะคอมไพล์ไม่ผ่าน

รูปแบบของพารามิเตอร์ list คือ signature ลองดูตัวอย่างต่อไปนี้

1. public void fooBar(int a, int b) 
2. int int fooBar(int foo, int bar) 
3. public void fooBar(int a, double b) 
4. internal int fooBar(int a, int b, int c) 

การจะพิจารณาว่า method สอง method มี signature ตรงกันหรือไม่ให้ดูที่พารามิเตอร์ ในกรณีตัวอย่างข้างบน method ที่ 1 และ 2 ถือว่าซ้ำกัน เพราะพารามิเตอร์เหมือนกันทุกประการ

signature ของ method ประกอบด้วยสิ่งต่างๆ ดังนี้
• ชื่อ method
• จำนวนพารามิเตอร์
• type ของพารามิเตอร์
• ชนิดของพารามิเตอร์ (คือ value, reference หรือ output)
• ลำดับของพารามิเตอร์ (เรียงจากซ้ายไปขวา)

signature ของ method ไม่รวมสิ่งต่างๆ ต่อไปนี้
• return type
• ชื่อของพารามิเตอร์
• modifier params (ซึ่งจะอยู่ที่ตำแหน่งขวาสุดในรายการของพารามิเตอร์)

เรื่องของ method signature มีความสำคัญเมื่อท่านต้องการทำ method overloading

 

method overloading

ในภาษา C# เราสามารถนิยาม method ชื่อเดียวกันหลายๆ แบบไว้ภายในคลาสเดียวกันได้ โดยมีข้อแม้ว่า signature ต้องไม่เหมือนกัน ความสามารถเช่นนี้ของภาษา C# มีประโยชน์มาก เพราะช่วยให้การใช้งาน type สะดวกขึ้น ภาษา C# ไม่เพียงสนับสนุนให้เราทำ method overloading (ต่อไปจะเรียกย่อว่า MO) เท่านั้น แต่ตัว .NET Framework เอง ยังใช้ MO อย่างกว้างขวางอีกด้วย ลองดูโค้ดตัวอย่างต่อไปนี้

1     using System;
2
3     namespace ConsoleApplication1
4     {
5        class Program
6        {
7           static void Main(string[] args)
8           {
9              Console.WriteLine(1);
10             Console.WriteLine("hello");
11             Console.WriteLine('c');
12             Console.WriteLine(123.456);
13          }
14       }
15    } 

ผู้เขียนเรียกใช้ method WriteLine() ซึ่งเป็น method ที่อยู่ในคลาส Console ของ namespace System ในบรรทัดที่ 9 ผู้เขียนเรียกใช้ method WriteLine() โดยส่งพารามิเตอร์ไปเป็นเลขจำนวนเต็ม ในบรรทัดที่ 10 ผู้เขียนเรียกใช้ method WriteLine() โดยส่งพารามิเตอร์ไปเป็น string ในบรรทัดที่ 11 ผู้เขียนเรียกใช้ method WriteLine() โดยส่งพารามิเตอร์ไปเป็น char ในบรรทัดที่ 12 ผู้เขียนเรียกใช้ method WriteLine() โดยส่งพารามิเตอร์ไปเป็นเลขแบบมีจุดทศนิยม เมื่อรันโปรแกรมนี้ ผลลัพธ์ที่ได้คือ

1
hello
c
123.456

ท่านสงสัยหรือไม่ว่า เพราะอะไรการเรียกใช้ method WriteLine()ในตัวอย่างข้างบนจึงไม่เกิด error ทั้งๆ ที่การส่งพารามิเตอร์แต่ละครั้งไม่เหมือนกันเลย คำตอบคือผู้สร้าง method WriteLine() ได้สร้าง method WriteLine() ไว้ในรูปแบบต่างๆ กันถึง 18 แบบ หรือพูดอีกอย่างหนึ่งคือทำ MO ไว้ 18 รูปแบบนั่นเอง ท่านคงเห็นแล้วว่าการทำ MO ช่วยให้การใช้งาน type สะดวกขึ้นมาก

ต่อไปนี้ผู้เขียนจะสาทิตวิธีเขียนโปรแกรมภาษา C# เพื่อสร้างและใช้ประโยชน์จาก MO ว่าทำได้ง่ายเพียงใด โปรดพิจารณาโค้ดตัวอย่างต่อไปนี้

using System; 

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Add(1));
            Console.WriteLine(Add(1, 2));
            Console.WriteLine(Add(1, 2, 3));
            Console.ReadLine();
        }
        static int Add(int i)
        {
            return i;
        } 
        static int Add(int i, int j)
        {
            return i + j;
        }
        static int Add(int i, int j, int k)
        {
            return i + j + k;
        }
    }
} 

จะเห็นว่าผู้เขียนนิยาม method สมาชิกชื่อ Add ขึ้นโดยทำ MO ไว้สามแบบ แบบแรกรับพารามิเตอร์ตัวเดียว แบบที่สองรับสองตัว และแบบที่สามรับสามตัว โปรแกรมที่เรียกใช้งาน method นี้สามารถเรียกใช้งาน method ได้โดยส่งพารามิเตอร์ตั้งแต่ 1 ถึง 3 ตัว เมื่อรันโปรแกรมนี้จะได้ผลลัพธ์ดังนี้

1
3
6

จะเห็นว่า method Add ทั้งสามแบบมี return type ตรงกันหมด (คือเป็น int) ในตัวอย่างนี้ชัดเจนว่า return type ควรเป็น int การทำ MO โดยให้ทุก method มี return type แบบเดียวกันหมดเป็นธรรมเนียมปฏิบัติตามปรกติ แต่ในบางกรณีเราอาจนิยามให้แต่ละ method มี return type แตกต่างกันก็ได้ ซึ่งควรทำด้วยความระมัดระวัง เพื่อหลีกเลียงปัญหาความไม่ลงรอยกันในการใช้งาน

 

การคอมไพล์

การคอมไพล์ (compile) คือการรวบรวม source code ทั้งหมดมาแปลงให้เป็นไฟล์ๆ เดียว โดยใช้โปรแกรมพิเศษที่เรียกว่า compiler เมื่อคอมไพล์แล้วผลลัพธ์ที่ได้จะเป็นไฟล์ assembly ซึ่งอาจเป็นไฟล์นามสกุล .exe หรือ .dll ก็ได้ ยกตัวอย่างเช่นโปแกรมต่อไปนี้

using System; 

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World");
        }
    }
} 

หากเราเก็บเป็น source code ชื่อ hello.cs เราสามารถคอมไพล์ได้โดยพิมพ์คำสั่งต่อไปนี้ที่ command prompt

csc hello.cs

ผลลัพธ์ที่ได้คือไฟล์ชื่อ hello.exe ไฟล์นี้เป็นโปรแกรมประยุกต์ที่เราสามารถนำไปรันในคอมพิวเตอร์เครื่องไหนก็ได้ที่ติดตั้ง .NET Framework ไว้

สมมุติว่าเราสร้างโปรแกรมไว้เพื่อต้องการใช้เป็น class library ดังนี้

using System; 

namespace CSharpBook
{
    class MyLib
    {
        public static string DecToHex(int dec)
        {
            uint uiDecimal = 0;
            uiDecimal = checked((uint)System.Convert.ToUInt32(dec.ToString()));
            return String.Format("{0:x2}", uiDecimal);
        }
    }
} 

หากเราเก็บเป็น source code ชื่อ csharpbook.cs เราสามารถคอมไพล์ให้เป็นไลบรารีได้โดยพิมพ์คำสั่งต่อไปนี้ที่ command prompt

csc /t:library csharpbook.cs

ผลลัพธ์ที่ได้คือไฟล์ชื่อ csharpbook.dll ไฟล์นี้เป็น class library ที่เราสามารถนำไปใช้งานร่วมกับ project อื่นที่เขียนด้วยภาษา C# หรือภาษา .NET อื่นๆ ได้ทุกภาษา

ตอนต่อไป: ตัวแปร – pointer – unsafe code

Advertisements

ตอน 21 Method

ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาในBlog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

Method

method (เมธอด) คือสมาชิกอีกแบบหนึ่งของคลาสและ struct ทำหน้าที่เป็นตัวประมวลผลเพื่อให้เกิดการทำงานตามความต้องการ method เป็นบล็อกของโค้ดที่มีคำสั่งเรียงกันตั้งแต่หนึ่งบรรทัดจนถึงหลายสิบบรรทัด บล็อกเริ่มจากเครื่องหมายปีกกาเปิด และจบบล็อกที่เครื่องหมายปีกกาปิด

ที่ส่วนหัวของ method เราต้องเขียนข้อกำหนดของ method ซึ่งประกอบด้วย access level(ระดับการเข้าถึง เช่น public, private) return type (เช่น int สำหรับเลขจำนวนเต็ม หากไม่มี return value ให้ใส่คำว่า void) ชื่อของ method และพารามิเตอร์ หากไม่มีพารามิเตอร์จะต้องใส่เครื่องหมายวงเล็บเปิดและปิดว่างๆ ไว้ หากมีพารามิเตอร์ต้องนิยาม type และชื่อของพารามิเตอร์ไว้ภายในวงเล็บ หากมีพารามิเตอร์หลายตัว ให้คั่นด้วยเครื่องหมายจุดลูกน้ำ (comma จุลภาค) โปรดพิจารณาตัวอย่างการนิยาม method ต่อไปนี้

    class StarShip
    {
        public void StartEngine() { }
        public void AddGas(int gallons) { }
        private int Drive(int miles, int speed) { return 0; }
    }

นี่คือนิยามคลาสชื่อ StarShip ภายในคลาสนี้มี method สมาชิกทั้งหมดสาม method คือ StartEngine, AddGass และ Drive ตามลำดับ แต่ละ method มีรายละเอียดการนิยามดังนี้

  • StartEngine: มี access level เป็น public มี return type เป็น void (คือไม่มี return type) และไม่มีพารามิเตอร์
  • AddGas: มี access level เป็น public มี return type เป็น void (คือไม่มี return type) มีพารามิเตอร์หนึ่งตัวเชื่อ gallons มี type เป็น int
  • Drive: มี access level เป็น private มี return type เป็น int มีพารามิเตอร์สองตัวคือ miles และ speed ซึ่งมี type เป็น int ทั้งคู่ โปรดสังเกตคำสั่ง return ซึ่งต้องมีหาก method นั้น return type ไม่ใช่ void

ตัวอย่างโค้ดที่เรียกใช้ method Drive เป็นดังนี้

    class Program
    {
        static void Main(string[] args)
        {
            SpaceShip myShip = new SpaceShip();
            int foo = myShip.Drive(10, 100);
        }
    }

เนื่องจาก method Drive เป็น method สมาชิกของคลาส SpaceShip เราจึงต้องสร้าง object ที่มี type เป็น SpaceShip ก่อนจึงจะเรียกใช้ method Drive ได้ การเรียกใช้ให้ใส่ชื่อ object ตามด้วยเครื่องหมายจุด ชื่อ method และตามด้วยพารามิเตอร์ในวงเล็บ โดยจำนวนของพารามิเตอร์ต้องตรงกับที่นิยามไว้

ต่อไปนี้เป็นข้อควรคำนึงบางประการเมื่อจะเขียนนิยาม method

  • เราสามารถประกาศตัวแปรภายใน method ได้ ตัวแปรที่ประกาศไว้ภายใน method จะถือว่าเป็นตัวแปรท้องถิ่นของ method (method local variable) ซึ่งจะเกิดและตายภายใน method เท่านั้น (คือมี ขอบเขต หรือ scope อยู่เพียงแค่ภายใน method)
  • การเขียนโปรแกรมภาษา C# เราอาจใช้ method อย่างพลิกแพลงตามหลักการ OOP ได้ครบทุกอย่างคือ
    • เราอาจนิยาม method ชื่อเดียวกัน ภายในคลาสเดียวกันหลายๆ ชุดได้ (แต่ signature ต้องไม่เหมือนกัน) เรียกการทำเช่นนี้ว่า method overloading
    • เราสามารถนิยาม method ใน derived class ซ้ำกับ method ใน base class ได้หากใช้ร่วมกับ keyword new เรียกการทำเช่นนี้ว่า method hiding
    • หาก method ใน base class เป็นแบบ virtual เราสามารถนิยาม method ใน derived class ซ้ำโดยให้เป็นแบบ override เรียกการทำเช่นนี้ว่า method overriding

โปรดอ่านรายละเอียดเรื่อง method overloading, method hiding และ method overriding ในหัวข้อของเรื่องนั้นๆ

 

Adres Hejlsberg บิดาแห่งภาษา C# ได้รับรางวัล Execllence in Programming ของนิตยสาร Dr. Dobb ในปี 2001

 

constructor

constructor (อ่านว่าคอนสทรักเตอร์ แปลว่าผู้สร้าง ต่อไปจะเรียกย่อว่า mc) คือ method พิเศษในคลาส ซึ่งจะทำงานทันทีที่เราสร้าง object โดยการใช้คำสั่ง new ลองดูตัวอย่างโค้ดต่อไปนี้

1  class FooBar
2  {
3      int i;
4      public FooBar()
5      {
6          i = 1;
7      }
8  }
9
10 class Program
11 {
12     static void Main(string[] args)
13     {
14         FooBar myFB = new FooBar();
15     }
16 } 

ผู้เขียนนิยามคลาส FooBar ไว้ โดยในคลาสนี้มีสมาชิกสองตัว เป็น filed หนึ่งตัว (บรรทัดที่ 3) และเป็น method หนึ่ง method (บรรทัดที่ 4 ถึง 7) method นี้คือ mc วิธีสังเกตว่า method ใดคือ mc ให้ดูที่ชื่อของ method ซึ่งจะตรงกับชื่อคลาสเสมอ

โปรแกรมบรรทัดที่ 10 ถึง 16 คือคลาส Program ซึ่งเรียกใช้ type FooBar ที่นิยามไว้ บรรทัดที่ 14 คำสั่ง

FooBar myFB = new FooBar();

เป็นรูปแบบมาตรฐานของการสร้าง object หรืออาจเขียนแยกเป็นสองบรรทัดเช่นนี้ก็ได้

FooBar myFB;

myFB = new FooBar();

บรรทัดแรก FooBar myFB; หมายถึงเราต้องการสร้างตัวแปรชื่อ myFB และกำหนด type ให้เป็น FooBar เหมือนเขียนว่า

int i;

หมายถึงเราต้องการสร้างตัวแปรชื่อ i และกำหนด type ให้เป็น int ในกรณีนี้ int คือ type ที่ถูกกำหนดไว้ใน .NET Framework class Library ส่วน FooBar คือ type ที่ถูกกำหนดไว้ในโปรแกรมของเราเอง โดยเราเป็นผู้สร้างนิยาม FooBar ไว้เอง

myFB = new FooBar();

คำสั่งนี้คือส่วนสำคัญที่เกี่ยวข้องกับ mc เพราะคำว่า FooBar() ที่เห็นในบรรทัดนี้คือ mc นั่นเอง บรรทัดบนเราสั่งว่า FooBar myFB; อย่างนี้ยังไม่มีการสร้าง object myFB เพียงถูกกำหนด type เท่านั้น และค่าของมันยังเป็น null อยู่ (คือยังไม่ได้อ้างอิงไปยัง object ใดๆ) object จะเกิดขึ้นก็ต่อเมื่อมีการเรียก mc โดยใช้คำสั่ง new และตามด้วยชื่อ method (ในตัวอย่างคือ new FooBar())

เมื่อคำสั่งนี้ทำงาน

FooBar myFB = new FooBar();

method FooBar() จะถูกเรียกให้ทำงาน และ CLR จะสร้าง object ใหม่ (หรือเรียกว่าสร้าง instance) จากคลาส FooBar (หรือที่เรียกว่าการทำ instantiate) นำ instance ที่ได้นี้ไปใส่ไว้ใน heap และกำหนดให้ตัวแปร myFB ทำหน้าที่ชี้ไปยังตำแหน่งของ object ใน heap

mc เป็นสิ่งจำเป็น เพราะหากไม่มี mc ก็จะไม่สามารถสร้าง object ได้ ดังนั้นหากท่านไม่เขียนนิยาม mc ไว้ C# จะสร้าง mc ให้โดยอัตโนมัติ (ในขณะ runtime) เรียกว่า default constructor ซึ่งเป็น mc แบบไม่มีพารามิเตอร์

ท่านสามารถสร้าง mc ได้หลายแบบโดยการทำ method overloading โปรดดูตัวอย่างโค้ดต่อไปนี้

1   using System;
2 
3   namespace ConsoleApplication1
4   {
5       class FooBar
6       {
7           string i;
8           string s;
9           public FooBar()
10          {
11              i = 1.ToString();
12          }
13          public FooBar(int x):this()
14          {
15              s = i;
16          }
17      }
18      class Program
19      {
20          static void Main(string[] args)
21          {
22              FooBar myFB = new FooBar(12);
23          }
24      }
25  }

นี่คือตัวอย่างการทำ mc overloading และทำ mc Chaining ด้วย คลาส FooBar มีนิยาม mc ไว้สองแบบๆ แรกไม่มีพารามิเตอร์ (บรรทัดที่ 9-12) แบบที่สองมีพารามิเตอร์หนึ่งตัวเป็น int (บรรทัดที่ 13-16)

ในกรณีที่มีการทำ mc overloading เป็นธรรมเนียมปฏิบัติที่จะนิยาม default mc ไว้เสมอ (คือ mc แบบไม่มีพารามิเตอร์) และให้ mc ตัวอื่นๆ เรียก default mc ให้ทำงานด้วย ในตัวอย่างโค้ดข้างบน บรรทัดที่ 13 คือนิยาม mc ที่ไม่ใช่ default mc และจะเรียก default me โดยใช้คำสั่ง :this()

ผลคือเมื่อสร้าง object จากคลา FooBar โดยการเรียก mc แบบมีพารามิเตอร์ (บรรทัดที่ 22) mc ทั้งแบบ default และแบบมีพารามิเตอร์จะถูกเรียกให้ทำงานทั้งคู่

ปรกติแล้วเราจะสร้างนิยามคลาสเพื่อนำไปใช้สร้าง object ดังนั้น mc จึงต้องมี access modifier เป็น public เพื่อให้ client class สามารถเรียกใช้งานได้ แต่มีบางครั้งที่เราไม่ต้องการให้นำคลาสไปใช้สร้าง object ในกรณีนี้จะต้องกำหนด access modifier ของ mc ให้เป็น private

 

destructor

ตรงข้ามกับ constructor คือ destructor (อ่านว่า เดสทรักเตอร์ แปลว่าผู้ทำลาย) เป็น method พิเศษที่จะทำงานก่อนที่ object จะถูกทำลายโดย Garbage Collector (GC)

ในภาษา C++ นักเขียนโค้ดจะต้องคอยระวังไม่ลืมที่จะทำลาย object ที่ไม่ได้ใช้งานเพื่อคืนหน่วยความจำให้แก่ระบบ หากลืมจะเกิดความผิดพลาดที่เรียกว่า memory leak ทำให้พื้นที่ในหน่วยความจำลดลงเรื่อยๆ ในกรณีที่เลวร้ายที่สุดอาจถึงกับทำให้ระบบหยุดทำงาน

ในภาษา C# นักเขียนโค้ดไม่ต้องกังวลเรื่องการทำลาย object และ memory leak เพราะโค้ดในภาษา C# เป็น managed code คือโค้ดที่ถูกจัดการดูแลโดย .NET Framework ซึ่งมีกลไก GC ทำหน้าที่คอยเวนคืนหน่วยความจำด้วยการทำลาย object ที่ไม่ได้ใช้งานแล้ว

อย่างไรก็ดี GC จัดการหน่วยความจำเฉพาะส่วน heap เท่านั้น ดังนั้นหากโปรแกรมของเรามีการใช้ทรัพยากรส่วนอื่นๆ นอกเหนือไปจากนั้น GC จะไม่สามารถจัดการได้ ด้วยเหตุนี้ภาษา C# จึงยังคงมี method destructor ไว้เพื่อให้เราจัดการเก็บกวาดได้ด้วยตนเอง โปรดพิจารณาโค้ดต่อไปนี้

class FooBar
{
    ~FooBar()
    {
        // ใส่โค้ดเพื่อปลดปล่อย resource ตรงนี้
    }
}

นี่คือคลาสที่มีสมาชิกเพียงตัวเดียวเป็น destructor โปรดสังเกตว่า

  • destructor คือ method ที่มีชื่อเดียวกับคลาส
  • หน้าชื่อต้องใส่เครื่องหมาย ~
  • destructor ไม่มี access modifier
  • destructor ไม่มี return type
  • destructor รับพารามิเตอร์ไม่ได้
  • ในหนึ่งคลาสมี destructor ได้ตัวเดียว
  • เราไม่สามารถทำ overload ต่อ destructor ได้

ตอนต่อไป:Method Parameter

ตอน 20 Assembly

ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาในBlog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

Assembly

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

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

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

เมื่อท่านคอมไพล์โปรแกรมให้เป็นโปรแกรมประยุกต์ ท่านจะได้ไฟล์นามสกุล .exe หากท่านคอมไพล์โปรแกรมให้เป็นไลบรารี (คือโปรแกรมที่ไม่มี method main()) ท่านจะได้ไฟล์นามสกุล .dll แม้ไฟล์ทั้งสองแบบนี้ จะมีนามสกุลเหมือนโปรแกรมแบบ Win32 แต่โครงสร้างภายในกลับไม่เหมือนกันเลย โดยไฟล์ assembly จะบรรจุโค้ดที่คอมไพล์แล้ว (เป็นภาษา MSIL ที่อยู่ในสภาพไบนารี) และข้อมูลแบบ metadata

 

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

metadata คือข้อมูลเลขฐานสองในรูปแบบ Portable Executable (PE) ข้อมูลนี้พรรณนาถึง type ภายใน assembly และ type ภายนอกที่โค้ดภายใน assembly เรียกใช้ ยกตัวอย่างเช่น metadata ของคลาส จะประกอบด้วยการพรรณนาถึงคลาสโดยละเอียด รวมถึงสมาชิกต่างๆ ภายในคลาส เช่น method พารามิเตอร์ของแต่ละ method เป็นต้น

ข้อดีประการสำคัญของ .NET คือ assembly แบบ .dll สามารถใช้งานข้ามภาษาได้ ยกตัวอย่างเช่นหากเพื่อนของท่านสร้าง type ด้วยภาษา VB.NET แล้วส่งให้ท่านเป็นไฟล์ .dll ท่านสามารถนำ type ในไลบรารีนั้นมาใช้งานในภาษา C# ได้โดยตรงราวกับเป็น type ที่สร้างด้วย C# ทุกประการ

Assembly คือ Portable executable file (PE) มีสองแบบคื่อ Process assembly (EXE) และ Libraly assembly (DLL) แบบ EXE จะสามารถนำไปคอมไพล์ (อีกครั้งโดย CLR) เพื่อใช้งานเป็นซอฟท์แวร์ประยุกต์ ขณะที่ DLL ใช้ทำไลบรารี นามสกุลไฟล์ไม่ใช่ตัวกำหนดชนิดของ PE สิ่งที่กำหนดคือ flag ใน PE ที่ compileจะตั้งให้เป็น process หรือ library ตามแต่เราจะสั่งตอนคอมไพล์

 

สมัยก่อน .NET การเขียนโปรแกรมแบบ Win32 โปรแกรมเมอร์ต้องลำบากในการควานหาฟังก์ชันที่ต้องการจากไฟล์ .dll จำนวนมาก ซึ่งไฟล์ .dll ของโปรแกรมประยุกต์ใน Win32 ถ้าจะให้ใช้งานร่วมกันได้ ก็จำเป็นต้องถูกจดทะเบียนเก็บรวมไว้ที่เดียวกันหมด (ปรกติจะอยู่ภายในโฟลเดอร์ Windows/system32) เมื่อมีโปรแกรมหลายตัวถูกติดตั้ง หรือเมื่อโปรแกรมเดิมเปลี่ยนเวอร์ชัน ความยุ่งยากที่เรียกว่า DLL Hell อาจเกิดขึ้นได้เสมอ

แนวคิดเรื่อง assembly เกิดขึ้นมาเพื่อแก้ปัญหา DLL Hell โดยตรง เพราะ DLL (หรือ EXE) แบบ .NET Framework จะถูกเก็บไว้ภายในโฟลเดอร์ของมันเอง ไม่ต้องมีการจดทะเบียนอะไรทั้งนั้น ดั้งนั้นโปรแกรมประยุกต์ใช้งานที่สร้างจากภาษา .NET ทั้งหมดจึงไม่ต้องการขบวนการติดตั้ง (setup หรือ install) ที่ยุ่งยากซับซ้อนเหมือนสมัย Win32 อันที่จริงแล้วเมื่อท่านต้องการติดตั้งโปรแกรมที่ท่านสร้างท่านสามารถใช้คำสั่ง xcopy คัดลอกไฟล์และโฟลเดอร์ทั้งหลายไปยังฮาร์ดดิสก์ของเครื่องปลายทางได้โดยตรง เมื่อต้องการยกเลิกการติดตั้ง (uninstall) ท่านก็เพียงแค่ลบไฟล์และโฟลเดอร์ที่เก็บโปรแกรมทิ้ง ไม่ต้องดำเนินการใดๆ กับ registry ของ Windows (เหมือนการติดตั้งโปรแกรมประยุกต์ในระบบปฏิบัติการของ Apple)

 

Field

field หรือ data field คือตัวแปรที่คู่กันกับคลาส หรือ instance ของคลาส (ก็คือ object) ใดๆ field อาจเป็นสมาชิกของคลาส หรือเป็นสมาชิกของ struct ก็ได้ field ทำให้เราสามารถสร้าง object ได้ตามหลักการ encapsulation โดยทำหน้าที่เก็บข้อมูลที่ใช้ในการประมวลผล field อาจมีภาวะเป็น object (reference type) หรือเป็นตัวแปรธรรมดา (value type) ก็ได้ โปรดดูตัวอย่างโค้ดต่อไปนี้

    class FooBar
    {
        int p;
        private object o;
    } 

คลาส FooBar มีสมาชิกเพียงสองตัว คือ p และ o ทั้งคู่เป็น field โดย p เป็นตัวแปรแบบ value type เหมือนตัวแปรธรรมดาทั่วไป ส่วน o เป็นตัวแปรแบบ object หรือตัวแปรแบบ reference type ในตัวอย่างข้างบน field o ถูกกำหนด access modifier เป็นแบบ private ส่วน p ไม่ได้ถูกกำหนด access modifier จึงกลายเป็น private ไปโดยปริยาย

ปรกติเราจะกำหนดให้ field มี access modifier ภาวะเป็น private เพื่อมิให้ client class หรือ derived class สามารถเข้าถึง field ได้ เมื่อต้องการให้ client class เปลี่ยนแปลงค่าของ filed เราจะให้ทำทางอ้อมผ่าน property แทน

อาจมีบางกรณีที่เราปรารถนาให้ client class สามารถอ่านค่าจาก field ได้ แต่ไม่ต้องการให้แก้ไขค่าของ field ในกรณีนี้เราจะกำหนด access modifier ของ field เป็น public และกำหนด modifier เป็น readonly ดังตัวอย่างต่อไปนี้

public readonly int foo;

ตัวอย่างโค้ดที่เห็นข้างบนเราประกาศ field ชื่อ foo ซึ่ง client class สามารถอ่านข้อมูลได้ แต่ไม่สามารถเขียนข้อมูล (เปลี่ยนแปลงข้อมูลใน field) ได้ (โปรดสังเกตลำดับการนิยามจะเริ่มจากซ้ายสุดเป็น access modifier ถัดมาเป็น type modifier และสุดท้ายติดกับชื่อ field คือ type)

filed ก็เหมือนตัวแปรทั่วไปที่เราสามารถกำหนดให้มี type เป็นอะไรก็ได้ เช่น int, string, double, decimal และเราอาจนิยาม field ให้เป็น array กี่มิติก็ได้

หากประสงค์จะนำคลาสไปใช้เป็น base class และต้องการให้ผู้นำคลาสไป inherit สามารถเข้าถึง field ได้ จะต้องกำหนด access modifier ของ field ให้เป็น protected field ที่มี access modifier แบบ protected จะสามารถเข้าถึงได้จาก derived class เท่านั้น โปรดดูตัวอย่างต่อไปนี้

1    class FooBar
2    {
3       protected int p;
4       public FooBar()
5       {
6          p = 12;
7       }
8    }
9    class NewFooBar : FooBar
10   {
11       public NewFooBar()
12       {
13          p++;
14       }
15       public int showP()
16       {
17          return p;
18       }
19    }
20    class Program
21    {
22       static void Main(string[] args)
23       {
24          NewFooBar myNFB = new NewFooBar();
25          Console.WriteLine(myNFB.showP());
26       }
27    } 

ในโค้ดนี้ base class คือ FooBar (บรรทัดที่ 1-8) คลาสที่สืบสันดานคือคลาส NewFooBar (บรรทัดที่ 9-19) และโค้ดที่ทดสอบการทำงานของ NewFooBar คือ method Main ในคลาส Program (บรรทัดที่ 20-27)

ในคลาส FooBar มีนิยาม field ชื่อ p ที่ถูกกำหนด access modifier ให้เป็น protected (บรรทัดที่ 3) ดังนั้นเมื่อคลาส NewFooBar อ้างถึง field ชื่อเดียวกัน (บรรทัดที่ 13) จะถือว่าเป็น field ตัวเดียวกัน เมื่อเราสร้าง object จากคลาส NewFooBar (บรรทัดที่ 24) ทั้ง constructor ของคลาส FooBar และ constructor ของคลาส NewFooBar จะถูกเรียกให้ทำงาน ผลลัพธ์ที่ปรากฏบนหน้าจอจึงเป็น

13

โปรดสังเกตว่าเราสร้าง method เพื่อให้ client class สามารถอ่านค่าจาก field ได้ เพราะ client class ไม่สามารถเข้าถึง field ที่มี access modifier เป็น private หรือ protect ได้โดยตรง method เช่นนี้ทำหน้าที่เหมือน property (ดูเรื่อง property ในหัวข้อถัดไป)

ข้อควรสังเกตอีกอย่างคือ derived class สามารถมองเห็นและใช้งาน field แบบ protected ที่นิยามไว้ใน base class ได้ราวกับเป็น filed ที่นิยามไว้ในตัว derived class เอง แต่จะไม่เป็นเช่นนั้นกับ filed แบบ private

ตามปรกติเมื่อเราสร้าง instance ของคลาสเราจะได้ object ในแต่ละ object จะมี filed ที่นิยามไว้ในคลาส เรียกว่า instance field สมมุติว่าเรานิยามคลาสให้มี field ชื่อ foo หากเราสร้าง instance ของคลาสนี้สิบ instance เราจะได้ object สิบตัว ใน object แต่ละตัวจะมี field ชื่อ foo เป็นของตัวเอง จึงเท่ากับเราได้ filed foo สิบตัวด้วย

ในทางกลับกัน หากเรากำหนดให้ field มี access modifier เป็น static จะทำให้ filed มีภาวะเป็น static field ซึ่งจะมีที่เก็บข้อมูลในหน่วยความจำเพียงตัวเดียวเท่านั้น ไม่ว่าเราจะสร้าง instance ของคลาสสักกี่ object ก็ตาม static field จะคงมีตัวเดียวเสมอ

 

ฟังก์ชันในภาษา C#

คำว่าฟังค์ชันในภาษา C และ C++ จะหมายถึงโปรแกรมย่อยหนึ่งโปรแกรมซึ่งทำหน้าที่ใดหน้าที่หนึ่ง เช่นฟังก์ชัน printf ทำหน้าที่แสดงผล แต่ในภาษา C# คำว่าฟังก์ชันกินความหมายกว้างกว่านั้น เพราะภาษา C# มีสมาชิกที่มีภาวะเป็นฟังค์ชันหลายแบบดังนี้

  • Method: ฟังก์ชันซึ่งเป็นตัวทำงานหลักของคลาส
  • Constructor: ฟังก์ชันที่ทำงานเมื่อมีการสร้าง instance ของคลาส
  • Destructor: ฟังก์ชันที่ทำงานเมื่อ object ถูกทำลาย
  • Property: ฟังก์ชันซึ่งทำหน้าที่อ่านเขียนค่ากับ client class
  • Indexer: ฟังก์ชันที่ทำให้เราจัดการ object ได้ราวกับเป็น array
  • Event: คือกลไกที่ช่วยให้ object สามารถส่งสัญญาณได้
  • Operator: ตัวกระทำเช่นบวก ลบ คูณ หาร

ฟังก์ชันในความหมายของโปรแกรมย่อยอย่าง printf ในภาษา C ในภาษา C# จะเรียกฟังก์ชันลักษณะนี้ว่า “สมาชิกแบบ method” เพราะในภาษา C# จะไม่มีฟังก์ชันลอยๆ อีกต่อไป ฟังก์ชันทุกฟังก์ชันจะต้องเป็นสมาชิกของคลาสใดคลาสหนึ่ง หรือ struct ใด struct หนึ่งเสมอ

 

properties

properties (อ่านว่า พร็อพเพอร์ตี) คือสมาชิกของคลาสซึ่งช่วยให้เรา อ่าน เขียน หรือคำนวณ ค่าของ private field ได้ แม้เราจะใช้งาน properties เหมือนกับว่ามันเป็นสมาชิกแบบ public data แต่อันที่จริงแล้วมันคือ method ชนิดพิเศษที่เรียกว่า accessors

ตามหลักการ encapsulation และ information hiding ของ OOP เราจะไม่อนุญาตให้ client class เข้าถึงส่วนเก็บข้อมูลของ object (คือ data filed) ได้โดยตรง เมื่อเราต้องการอ่านหรือเขียนค่าของ data filed ใน object เราจะใช้ properties ทำหน้าที่เป็นตัวกลางในการเชื่อมต่อแทน โปรดพิจารณาโค้ดตัวอย่างต่อไปนี้

1     class FooBar
2     {
3         private int power = 12;
4
5         public int Power
6         {
7             get { return power; }
8             set { power = value; }
9         }
10    }
11    class Program
12    {
13       static void Main(string[] args)
14       {
15           FooBar myFooBar = new FooBar();
16           Console.WriteLine(myFooBar.Power);
17           myFooBar.Power = 5;
18           Console.WriteLine(myFooBar.Power);
19           Console.ReadLine();
20       }
21    } 

โค้ดข้างบนนี้สาทิตการนิยาม properties อย่างง่ายที่สุด ผู้เขียนสร้างนิยามคลาสชื่อ FooBar (บรรทัดที่ 1-10) โดยนิยามสมาชิกเพียงสองตัว ตัวแรกชื่อ power เป็น data field (บรรทัดที่ 3) และกำหนดค่าเริ่มต้นให้เป็น 12 แม้ว่าผู้เขียนจะต้องการให้ client class สามารถเปลี่ยนแปลงค่าของ power ได้ แต่ก็ไม่ต้องการให้เข้าถึง power ได้โดยตรง จึงกำหนด access modifier ของ power ให้เป็น private

จากนั้นผู้เขียนสร้างนิยาม properties ชื่อ Power ซึ่งจะทำหน้าที่ให้ client class สามารถ อ่าน และเปลี่ยนแปลงค่าของ power ได้

โปรดสังเกตว่า power ตัวที่เป็น data field เขียนด้วยตัวเล็กทั้งหมด ส่วน Power ตัวที่เป็น properties เขียนโดยให้ตัวอักษรนำเป็นตัวใหญ่และตัวตามเป็นตัวเล็ก เนื่องจากภาษา C# เป็นภาษา case sensitive (แยกแยะตัวอักษรใหญ่-เล็กว่าต่างกัน) จึงมองว่า power และ Power เป็นชื่อที่ไม่ซ้ำกัน

Class diagram (แผนภูมิแสดงคลาส) ของคลาส FooBar และคลาส Program ตามโค้ดตัวอย่างข้างบน Fields แสดงด้วยภาพสี่เหลี่ยมสีน้ำเงิน method เป็นสี่เหลี่ยมสีชมภู และ Property เป็นภาพมือจับเอกสาร

 

การตั้งชื่อ data field และ properties ที่คู่กันให้มีชื่อตรงกัน แล้วทำให้แตกต่างกันโดยใช้ตัวอักษรนำเป็นธรรมเนียมปฏิบัติ (best practice) ที่ใช้กันตามปรกติ เพราะช่วยป้องกันให้ไม่เกิดความสับสนในการเขียนโปรแกรม แต่เราสามารถชื่อ data field และ properties ที่คู่กันให้มีชื่อแตกต่างกันโดยสิ้นเชิงก็ได้

keyword get และ return ในบรรทัดที่ 7 จะใช้คู่กันเสมอเพื่อให้ client class สามารถอ่านค่าของ data field ได้ keyword set และ value ในบรรทัดที่ 8 จะใช้คู่กันเสมอเพื่อให้ client class สามารถเปลี่ยนแปลงค่าของ data field ได้

ในตัวอย่างนี้โค้ดที่สาทิตการใช้งานคลาส FooBar คือ method Main ซึ่งเป็น method สมาชิกของคลาส Program โดยสร้าง object ชื่อ myFooBar (บรรทัดที่ 15) แล้วอ่านค่า data field power ผ่าน properties Power โดยใช้ชื่อ object ตามด้วยจุด และชื่อของ properties (myFooBar.Power บรรทัดที่ 16) การทำเช่นนี้จะทำให้โค้ดในคลาส FooBar บรรทัดที่ 7 ทำงาน คำสั่ง get จะนำค่าของ power ส่งกลับมาด้วยคำสั่ง return ซึ่งต้องมี return type ตรงกันทั้ง data field และ properties (ในตัวอย่างนี้เป็น int)

บรรทัดที่ 17 สาทิตการเปลี่ยนแปลงค่าของ data field power ผ่านทาง properties Power โดยใช้คำสั่ง

myFooBar.Power = 5;

การทำเช่นนี้มีผลให้โค้ดในคลาส FooBar บรรทัดที่ 8 ทำงาน คำสั่ง set จะนำเลข 5 ไปเป็นค่าของ value ซึ่งถูกนำไปกำหนดเป็นค่าของ data field power อีกต่อหนึ่ง

เมื่อรันโปรแกรมนี้ ผลลัพธ์บนจอภาพจะเป็นดังนี้

12

5

โปรดสังเกตว่า access modifier ของ properties จะเป็น public เสมอ

properties ช่วยให้เราสามารถคำนวณ เปลี่ยนแปลง หรือควบคุมค่าที่ client class ส่งมาได้ง่าย ยกตัวอย่างเช่น หากเราต้องการควบคุมไม่ให้ค่าของ power สูงกว่า 100 เราสามารถกำหนดใน properties Power ได้ดังนี้

    class FooBar
    {
        private int power = 12;

        public int Power
        {
            get { return power; }
            set
            {
                if (value < 100)
                {
                    power = value;
                }
                else
                {
                    power = 100;
                }
            }
        }

    }
    class Program
    {
        static void Main(string[] args)
        {
            FooBar myFooBar = new FooBar();
            Console.WriteLine(myFooBar.Power);
            myFooBar.Power = 50;
            Console.WriteLine(myFooBar.Power);
            myFooBar.Power = 150;
            Console.WriteLine(myFooBar.Power);
            Console.ReadLine();
        }
    }

ภายใน properties ส่วน get-value ผู้เขียนเพิ่มโค้ดตรวจสอบว่าค่าที่ส่งมาเกิน 100 หรือไม่ หากเกินให้กำหนดค่า power ไว้ที่ 100 เท่านั้น เมื่อรันโปรแกรมนี้จะได้ผลลัพธ์บนจอภาพดังนี้

12

50

100

ตอนต่อไป: Method

ตอน 19 Statement


ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาในBlog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

“คุณจะรู้ตัวว่าคุณเขียนโปรแกรมมากเกินไปเมื่อคุณพยายามไล่แมลงวันที่เกาะอยู่บนหน้าจอด้วยเมาส์เคอร์เซอร์ สิ่งนี่เพิ่งเกิดกับตัวของผมเอง มันน่ากลัวมาก”

Juuso Heimonen

Statement บทที่ 4 ภาษา C# ระดับปานกลาง

ในบทนี้ท่านจะได้เรียนเรื่องต่อไปนี้

  • Statement
  • สมาชิกของคลาสและ struct
  • Access modifier
  • Type modifier
  • Assembly
  • Field
  • properties
  • Method
  • constructor
  • destructor
  • method Parameter
  • method Signature
  • method overloading

 

Statement

อันที่จริง Statement (อ่านว่า สเต็ทเม็นท์ แปลว่าประโยคคำสั่ง) ในภาษา C# ไม่แตกต่างจากประโยคในภาษามนุษย์มากนัก แต่ก็เหมือนภาษาคอมพิวเตอร์อื่นๆ ที่รูปแบบของประโยคจะซับซ้อนน้อยกว่าภาษามนุษย์ แต่มีกฎเกณฑ์ที่เคร่งครัดกว่ามาก ยกตัวอย่างประโยคภาษามนุษย์

   ฉันชอบแมว

ใครก็ตามที่อ่านประโยคข้างบนจะเข้าใจได้ทันที แต่หากผู้เขียนเขียนเพียงว่า

   ฉันชอบ

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

   int foo = bar + 2; 

อย่างนี้คอมไพเลอร์อ่านแล้วจะเข้าใจและสามารถแปลเป็นภาษา MSIL ได้ทันที ความหมายของคำสั่งนี้คือ ให้นำค่า 2 มาบวกกับค่าที่อยู่ในตัวแปรชื่อ bar จากนั้นให้นำไปเก็บไว้ในตัวแปรแบบเลขจำนวนเต็มที่สร้างใหม่ชื่อ foo

โปรดสังเกตเครื่องหมาย ; (semi-colon อ่านว่าเซมิ-โคลอน เครื่องหมายอัฒภาค) ว่าทำหน้าที่เหมือนเครื่องหมาย full stop (เครื่องหมายจุด คือมหัพภาค) ภาษาอังกฤษ คือทำหน้าที่บอกการจบประโยค

หากผู้เขียนเขียนว่า

int foo =

อย่านี้ถือว่าไม่จบประโยค เพราะไม่รู้ว่า foo เท่ากับอะไร คอมไพเลอร์จะไม่สามารถแปลคำสั่งได้ และจะแสดงข้อความแจ้งความผิดพลาด แต่ถ้าผู้เขียนเขียนว่า

   int foo = 
   bar + 1; 

อย่างนี้ถือว่าใช้ได้ เพราะ statement ในภาษา C# เราจะกด enter เพื่อขึ้นบรรทัดใหม่อย่างไรก็ได้ตามใจชอบ คอมไพเลอร์คงถือว่าทุกบรรทัดเป็น statement เดียวกัน จนกว่าจะพบเครื่องหมาย semi-colon จึงจะนับว่าจบ statement

คอมพิวเตอร์จะทำงานตามคำสั่งแต่ละ statement จากบนสุดลงล่าง ยกตัวอย่างเช่น

public Bar()
{
   statement1
   statement2
   statement3
}

คอมพิวเตอร์จะทำงานตามคำสั่งใน statement1 ก่อน เมื่อจบแล้วจะทำงานตามคำสั่งใน statement2 และ statement3 ตามลำดับ ยกเว้นว่าจะพบ statement ที่มีคำสั่งให้วนซ้ำ ข้าม หรือกระโดดไปยัง statement อื่นเช่นคำสั่ง goto เป็นต้น

Anders Hejlsberg บิดาแห่งภาษา C# ขณะเป็น Austin Powers

 

สมาชิกของคลาสและ struct

เมื่อเขียนโปรแกรมภาษา C# เราใช้เวลาส่วนมากไปกับการสร้าง type การสร้าง type คือการนิยามคลาส หรือ struct เมื่อนิยามคลาสและ struct เราใช้เวลาส่วนมากไปกับการประกาศ และนิยามสมาชิกของมัน

สมาชิกของคลาสและ struct อาจประกอบด้วยสิ่งต่างๆ ดังนี้

  • constants
  • fields
  • methods
  • properties
  • events
  • indexers
  • operators
  • instance constructors
  • destructors
  • static constructors
  • type

ในกรณีที่นิยามคลาสโดยสืบสันดานจาก base class สมาชิกของคลาส อาจประกอบด้วยสิ่งต่างๆ ดังนี้

  • constants
  • fields
  • methods
  • properties
  • events
  • indexers
  • operators
  • type จาก base class

ลองดูตัวอย่างโค้ดดังต่อไปนี้

 

    class Foo
    {
        int i;
        public int I
        {
            get { return i; }
            set { i = value; }
        }
        struct Bar
        {
            int a;
            int b;
        }
        public Foo()
        {
            int var1;
        }
        private void Method1()
        {
            int i;
        }
    }

ผู้เขียนจะใส่เลขบรรทัดเพื่อให้สะดวกในการอธิบายดังนี้

1    class Foo
2    {
3       int i;
4       public int I
5       {
6          get { return i; }
7          set { i = value; }
8       }
9    struct Bar
10   {
11      int a;
12      int b;
13   }
14   public Foo()
15   {
16      int var1;
17   }
18   private void Method1()
19   {
20      int i;
21   }
22 }

 

นี่คือตัวอย่างการนิยามคลาสชื่อ Foo ภายในคลาสนี้ ผู้เขียนนิยามสมาชิกของคลาสไว้ห้าตัวคือ

  1. บรรทัดที่ 3 data field ชื่อ i
  2. บรรทัดที่ 4 property ชื่อ I
  3. บรรทัดที่ 9 struct ชื่อ Bar
  4. บรรทัดที่ 14 instance constructor ชื่อ Foo()
  5. บรรทัดที่ 18 method ชื่อ Method1

โปรดสังเกตว่าในคลาสนี้มีการนิยามตัวแปรชื่อ i สองตัว บรรทัดที่ 3 และบรรทัดที่ 20 ตัวแปรสองตัวนี้แม้จะมีชื่อเดียวกันและอยู่ในคลาสเดียวกัน แต่ก็เป็นคนละตัวกัน โดยตัวแปร i ในบรรทัดที่ 3 มีภาวะเป็น class data field ขณะที่ตัวแปร i ในบรรทัดที่ 20 ทำหน้าที่เป็น method local variable การเปลี่ยนแปลงค่าตัวแปร i ตัวหนึ่งจะไม่มีผลกระทบกับตัวแปร i อีกตัวหนึ่ง

โค้ดที่อยู่ภายนอกคลาส Foo จะ “เข้าถึง” (access คืออ่านหรือเขียนค่าหรือเรียก method) สมาชิกต่างๆ ของคลาส Foo ไม่ได้ นอกเสียจากว่าเราจะกำหนด “ตัวกำหนดการเข้าถึง” (access modifier) ไว้

 

Access modifier

access modifier คือ keyword ทำหน้าที่ประกาศว่า client class จะเข้าถึงสมาชิกของ type ได้หรือไม่อย่างไร access modifier มีสี่ตัวดังนี้

  • public : เข้าถึงได้โดยไม่มีข้อจำกัดใดๆ
  • protected : เข้าถึงได้จากในคลาสนั้นและคลาสที่สืบสันดานไปจากคลาสนั้น
  • internal : เข้าถึงได้จากทุกไฟล์ใน assembly เดียวกัน (เหมือน friend ใน VB.NET และ C++)
  • private : client class เข้าถึงไม่ได้ เป็นสมาชิกที่ใช้ได้ภายในคลาสนั้นเท่านั้น

ลองดูโค้ดตัวอย่างต่อไปนี้

 

    class Foo
    {
        int bar1;
        private int bar2;
        public int bar3;
        protected int bar4;
        internal int bar5;
    }
ผู้เขียนจะใส่เลขบรรทัดเพื่อให้สะดวกในการอธิบายดังนี้

1    class Foo
2    {
3       int bar1;
4       private int bar2;
5       public int bar3;
6       protected int bar4;
7       internal int bar5;
8    }

 

ในตัวอย่างนี้ผู้เขียนนิยามคลาสชื่อ Foo ที่มีสมาชิกเป็น data filed ห้าตัว เป็นตัวแปรแบบเลขจำนวนเต็ม ชื่อ bar1 ถึง bar5 โดยกำหนด access modifier แต่ละตัวไว้ดังนี้

  • ตัวแปร bar1 ไม่ได้กำหนด access modifier จะกลายเป็น private โดยอัตโนมัติ
  • ตัวแปร bar2 กำหนด access modifier เป็น private เข้าถึงได้เฉพาะในคลาส Foo
  • ตัวแปร bar3 กำหนด access modifier เป็น public client class เข้าถึงได้ไม่จำกัด
  • ตัวแปร bar4 กำหนด access modifier เป็น protected เข้าถึงได้เฉพาะในคลาส Foo และคลาสที่สืบสันดานจากคลาส Foo
  • ตัวแปร bar5 กำหนด access modifier เป็น internal เข้าถึงได้จากโค้ดที่อยู่ภายใน assembly เดียวกับคลาส Foo

นอกจาก access modifier แล้ว ภาษา C# ยังมี modifier อื่นๆ เพื่อใช้แก้ไขเปลี่ยนแปลงการประกาศ type ดังจะกล่าวถึงในหัวข้อถัดไป

 

 

Type modifier

เราสามารถนิยาม type ในภาษา C# ได้อย่างพลิกแพลง โดยการกำหนด modifier ซึ่งในหัวข้อก่อนหน้านี้ผู้เขียนอธิบาย access modifier ไปแล้ว ในหัวข้อนี้เราจะมาดู modifier แบบอื่นๆ ที่เหลือทั้งหมด

  • abstract ใช้เพื่อระบุว่าคลาสนี้มีไว้ใช้ทำเป็น base class
  • const ประกาศสร้างตัวคงค่า
  • event ประกาศสร้าง event
  • extern อ้างถึง method ที่อยู่ภายนอก assembly
  • new ใช้เมื่อสืบสันดานแล้วต้องการซ่อนสมาชิกของ base class
  • override ใช้เมือสืบสันดานแล้วต้องเขียนนิยามใหม่ทับสมาชิกของ base class
  • partial ระบุว่า type นี้ประกอบด้วยไฟล์ source code หลายไฟล์
  • readonly อ่านได้ เขียนไม่ได้
  • sealed ใช้เพื่อระบุว่าจะนำคลาสนี้ไปใช้เป็น base class ไม่ได้
  • static ใช้ระบุว่าสมาชิกนั้นๆ เป็นของคลาส ไม่ได้เป็นของ object
  • unsafe ประกาศบริเวณ unsafe
  • virtual ใช้ระบุว่าสมาชิกนั้นๆ ให้นำไป override ได้
  • volatile อนุญาตให้ระบบปฏิบัติการหรือ thread อื่นแก้ไขตัวแปรชนิดนี้ได้

หากท่านอ่านคำอธิบายย่อๆ ข้างบนแล้วยังไม่เข้าใจชัดเจนก็ไม่เป็นไร เพราะในภาคปฏิบัติ ไปผู้เขียนจะอธิบาย modifier แต่ละตัวอีกครั้งเมื่อเราพบมันภายในโค้ดที่ใช้งานจริง

ตอนต่อไป : Assembly

ตอน 18 : นิพจน์ในภาษา C# (Expression)


ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาใน Blog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

 

นิพจน์ในภาษา C# (Expression)

ในทางคณิตศาสตร์ 2+2 หรือ x-y คือตัวอย่างของนิพจน์ง่ายๆ ในภาษา C# นิพจน์คือการรวมตัวกันของ value (เช่น เลข 2) ตัวแปร (เช่น Foo) และตัวกระทำ (เช่น ลบ -) เมื่อนำมารวมเข้าด้วยกันสามารถผลิตค่าใหม่ออกมาได้ ยกตัวอย่างเช่น

x = i + 10;

นี่คือคำสั่งที่สมบูรณ์หนึ่งบรรทัด ส่วนที่เป็นนิพจน์คือส่วนที่อยู่ทางขวาของเครื่องหมายเท่ากับ (คือ i + 10) ค่าที่มันผลิตได้ถูกนำไปเก็บไว้ในตัวแปร x

ต่อไปนี้เป็นตัวอย่างของนิพจน์ในภาษา C#

a + 1
a++
a+
+a
i > a
i ==
(x * y) + (z * 2)
foo << 2
bar is int

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

ตัวกระทำแบบฝ่ายเดียว (unary operator ยูนารีโอเปอร์เรเตอร์) ทำให้เขียนโปรแกรมได้กระชับไม่เยิ่นเย้อ ยกตัวอย่างเช่น ในภาษา VB หากต้องการเพิ่มค่าของตัวแปร i ขึ้นหนึ่ง (คือหากเดิม i เป็น 1 จะกลายเป็นสอง) จะต้องเขียนว่า

i = i + 1;

ในขณะที่ภาษา C# เขียนเพียง

i++ ;

อีกตัวอย่างของการใช้ unary operator คือ

if (!ProductFound(“001”))

นี่คือการใช้ตัวกระทำ ! เพื่อกลับค่าตรรกะของ method ProductFound() สมมุติว่าเราสร้าง method ชื่อ ProductFound() ซึ่งทำหน้าที่ค้นหาสินค้าในฐานข้อมูลตามเลขประจำตัว โดยมี return type เป็นบูลลีน หากหาพบข้อมูลจะส่งค่ากลับมาเป็น true (จริง) มิฉะนั้นจะส่งค่า false (เท็จ) กลับมา ในตัวอย่างโค้ดข้างบนการใส่ unary operator ! ทำให้ความหมายกลายเป็น “หากไม่พบสินค้าหมายเลข 001”

ตัวกระทำแบบสองฝ่าย (Binary operator) เป็น operator ที่เราคุ้นเคยที่สุด ยกตัวอย่างเช่น

a = b + c;

คำสั่งบรรทัดนี้นำค่าในตัวแปร b และ c มาบวกกันแล้วนำผลลัพธ์ที่ได้ไปเก็บไว้ในตัวแปร a

x = y * 2;

คำสั่งบรรทัดนี้นำค่าในตัวแปร y ไปคูณกับ 2 แล้วนำผลลัพธ์ที่ได้ไปเก็บไว้ในตัวแปร x

ตัวกระทำแบบสามฝ่าย (Ternary operator) operator แบบนี้เราไม่ค่อยได้ใช้บ่อยนัก มันช่วยทำให้โค้ดกระชับ แต่อ่านเข้าใจยาก ยกตัวอย่างเช่น

s = x !=0 ? 1 : 2;

หมายถึงถ้า x ไม่ได้มีค่าเป็นศูนย์ให้นำเลข 1 ไปใส่ตัวแปร s มิฉะนั้นให้ s เท่ากับ 2 ปรกติเราเขียนโดยใช้คำสั่ง if ได้ดังนี้

if (x != 0)
{
     s = 1;
}
else
{
     s = 2;
}

จะเห็นว่าการใช้ Ternary operator เขียนโค้ดได้สั้นกว่ามาก แต่เวลาอ่านอาจต้องใช้เวลาชั่วขณะกว่าจะเข้าใจ ขณะที่การใช้ if แค่มองผ่านๆ ก็เข้าใจได้อย่างรวดเร็ว

ตัวกระทำ หรือ Operator ในภาษา C# เป็นสิ่งที่ดีเด่น น่าสนใจ สนุก และมีประโยชน์ ในตารางนี้เป็น predefined operator หรือตัวกระทำที่ถูกกำหนดหน้าที่มาแล้ว แต่เราอาจทำ overload ต่อตัวกระทำเหล่านี้ได้ตามใจชอบ

 

การประกาศ (Declaration)

เวลาที่เราใช้ไปในการเขียนโปรแกรมภาษา C# ส่วนใหญ่หมดไปกับการเขียนนิยามของคลาส ในนิยามของคลาสจะเต็มไปด้วยการประกาศสิ่งต่างๆ เช่น field method property event indexer และอื่นๆ การประกาศเหล่านี้เราต้องตั้งชื่อสิ่งต่างๆ เองทั้งหมด ชื่อทั้งหมดต้องเป็นตัวอักษรในภาษาอังกฤษ และมีระเบียบการตั้งชื่อให้เหมาะสม (ดูหัวข้อการตั้งชื่อในตอนที่แล้ว) ตัวอย่างการประกาศดังนี้

int foo;

อย่างนี้เป็นการประกาศตัวแปรชื่อ foo ให้มีชนิดเป็นเลขจำนวนเต็ม (integer) โดยไม่ได้ระบุขอบเขตการเรียกใช้ (access modifier) ทำให้มันมีขอบเขตฯ เป็นแบบ private ไปโดยอัตโนมัติ

public class ShowThumb

โค้ดบรรทัดนี้เป็นตัวอย่างการประกาศคลาสชื่อ ShowThumb

เราจะตั้งชื่อซ้ำกันในพื้นที่ (declaration space) เดียวกันไม่ได้ ยกตัวอย่างเช่นใน method หนึ่งจะประกาศตัวแปรสองตัวที่มีชื่อเดียวกันไม่ได้ หรือจะประกาศคลาสสองคลาสที่มีชื่อเดียวกันภายใน name space เดียวกันไม่ได้

เราจะประกาศตัวแปรตามลำดับอย่างไรก็ได้ตามใจชอบ แต่จะเรียกใช้ตัวแปรก่อนการประกาศตัวแปรนั้นๆ ไม่ได้ ตัวแปรท้องถิ่น (เช่นตัวแปรภายใน method) จะมีขอบเขตอยู่ภายใน block นั้นๆ จะถูกสร้างเมื่อโปรแกรมทำงานมาถึงคำสั่งบรรทัดที่ประกาศตัวมัน และจะถูกทำลายเมื่อโปรแกรมออกจาก block ที่มันอาศัยอยู่ กรุณาดูตัวอย่างโค้ดต่อไปนี้

for (int i = 0; i < 10; i++)
{
     Console.WriteLine(i.ToString())
}

ในตัวอย่างนี้ block คือ block ของคำสั่ง for ซึ่งอยู่ระหว่างวงเล็บปีกกาทั้งสอง ที่บรรทัดคำสั่ง for มีการสร้างตัวแปรแบบ int ชื่อ i ซึ่งจะเรียกใช้งานได้ภายใน block ของ for เท่านั้น เมื่อ for จบการทำงานและออกจาก block นี้ตัวแปร i จะหายไป

 

ไบรอัน ดับเบิลยู เคอร์นิฮัน (Brian Wilson Kernighan) นักวิทยาศาสตร์คอมพิวเตอร์แห่งห้องทดลองเบลล์ เขาเป็นผู้ร่วมเขียนตำรา The C Programming Language และเป็นคนแรกที่ริเริ่มธรรมเนียม Hello, world 

 

array

array คือตัวแปรตัวเดียวแต่เก็บข้อมูลได้เป็นชุดเหมือน table ในฐานข้อมูล คือสามารถกำหนดให้มีกี่ column กี่ row ก็ได้ ยกตัวอย่างเช่น

int[] foo;

foo = new int[5];

คำสั่งสองบรรทัดนี้ประกาศ array ซึ่งมี type เป็น int มีขนาด column เดียว จำนวนห้า row หรือเรียกอีกอย่างหนึ่งว่าเป็น array มิติเดียว (single dimension) row แต่ละ row ใน array เรียกว่า array element (เรียกย่อๆ ว่า AE) วิธีนำ array ไปใช้งานเป็นดังนี้

foo[0] = 12;
foo[1] = 5;
foo[2] = 123;
foo[3] = 777;
foo[4] = 987;

การใช้งาน array เราสามารถอ้างถึง AE ของ array ได้โดยใช้วงเล็บเหลี่ยมตามด้วยตัวเลขระบุ AE เรียกว่า array index (เรียกย่อว่า AI) ในภาษา C# AI จะเริ่มด้วยเลขศูนย์ ดังนั้นในตัวอย่างนี้เราประกาศ array ขนาด 5 AE ดังนั้น AI จึงมีค่า 0 ถึง 4

โปรดอย่าสับสนระหว่างตัวเลขกำหนด AE ขณะประกาศ array กับตัวเลข AI ขณะใช้งาน array แม้ทั้งสองจะอยู่ในวงเล็บเหลี่ยมเหมือนกัน แต่เป็นคนละเรื่องกัน ให้จำไว้ว่า AI แรกจะเท่ากับ 0 และ AI สุดท้ายย่อมเท่ากับ AE – 1 เสมอ

เราสามารถนำกลไก array มาใช้กับ object ใดๆ ก็ได้ แม้แต่ object ที่เรานิยามขึ้นเอง โปรดพิจารณาตัวอย่างโค้ดต่อไปนี้

1   public class test
2   {
3      string s;
4      public string S
5      {
6         get { return s; }
7       set { s = value; }
8      }
9   }
10
11 class Program
12 {
13    static void Main(string[] args)
14    {
15    test[] myTest;
16    myTest = new test[10];
17
18    for (int i = 0; i < 3; i++)
19       myTest[i] = new test();
20
21    myTest[0].S = "aaa"
22    myTest[1].S = "bbb"
23    myTest[2].S = "ccc"
24
25    for (int i = 0; i < 3; i++)
26       Console.WriteLine(myTest[i].S);
27    Console.ReadLine();
28    }
29 }

โค้ดที่เห็นข้างบรรทัดที่ 1 ถึง 9 นิยามคลาสชื่อ test ซึ่งเป็นคลาสที่มีสมาชิกสองตัวคือ filed s และ property S ไว้ใช้งานคู่กัน ถัดบรรทัดที่ 11 ถึง 30 คือคลาส Program ซึ่งทำหน้าที่เป็น client class เรียกใช้ test บรรทัดที่ 15 สร้าง array ชื่อ myTest เป็นตัวแปรอ้างอิง object กำหนดให้มี type เป็น test และใส่เครื่องหมายวงเล็บเหลี่ยมไว้หลัง test ด้วยเพื่อระบุว่านี่เป็นการประกาศ array

บรรทัดที่ 16 กำหนดจำนวน AE ของ array ให้มีขนาด 10 AE บรรทัดที่ 18,19 สร้าง instance ของคลาส test เป็น array ของ object จำนวน 3 ตัว (อันที่จริงเป็น array ของตัวแปรที่ใช้อ้างอิง object และในคำสั่งสองบรรทัดนี้เราสร้าง instance ของ object ขึ้นมาสาม instance ด้วยเช่นกัน) บรรทัด 21, 22, 23 set property S ของ object แต่ละตัวให้เป็น aaa,bbb และ ccc ตามลำดับ เพื่อเป็นการทดสอบว่า object แต่ละสามารถเก็บข้อมูลได้ถูกต้องหรือไม่ บรรทัด 25,26

จะเห็นว่าการสร้าง array ของ object ทำได้ง่ายพอๆ กับการสร้าง array ของ value type โปรดอย่าสับสนระหว่าง object array กับ indexer เพราะเป็นคนละเรื่องกัน indexer เป็นการทำงานกับกลุ่ม object ย่อย ที่อยู่ภายใน object หลัก (กรุณาดูเรื่อง indexer ในบทที่ 5)

 

สรุปท้ายบท

ทั้งหมดในบทนี้คือพื้นฐานภาษา C# หากท่านเคยเขียนโปรแกรมภาษา C, C++ Java หรือ PHP มาก่อนท่านจะพบว่าบทนี้ไม่มีอะไรแปลกใหม่มากนัก แต่ถ้าท่านเป็นมือใหม่ เนื้อหาในบทนี้นับว่าเข้มข้นและมีสิ่งใหม่ที่ต้องเรียนรู้มาก หากท่านไม่เข้าใจทุกอย่างโดยสมบูรณ์ก็ไม่เป็นไร เพราะเรื่องทั้งหมดนี้ผู้เขียนจะอธิบายอีกครั้งในภาคปฏิบัติ บทต่อไปเป็นเรื่องภาษา C# ที่ลึกขึ้นอีกระดับ แต่ก็เป็นเรื่องที่นักเขียนโปรแกรมภาษา C# ทุกคนจำเป็นต้องรู้

 

คำถามท้ายบท

  1. อะไรทำให้ภาษา C# เป็นภาษาที่ทนทาน
  2. คุณสมบัติอะไรที่มีในภาษา C# แต่ไม่มีในภาษา C++
  3. จงเขียนโปรแกรม hello world และทดสอบโปรแกรม
  4. คุณสมบัติอะไรในภาษา C# ที่คล้ายคุณสมบัติที่มีในภาษา Python
  5. จงอธิบายความหมายของคำว่า code behind
  6. Value Type คืออะไร
  7. Reference Type คืออะไร
  8. การทำ Boxing และ Unboxing คืออะไร
  9. ข้อแตกต่างที่สำคัญระหว่างคลาสและ struct คืออะไร
  10. นิพจน์คืออะไร

ตอนต่อไป : Statement

ตอน 17: หลักการตั้งชื่อในภาษา C#

ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาใน Blog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

 

หลักการตั้งชื่อในภาษา C#

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

วิธีตั้งชื่อที่ดีในการเขียนโปรแกรมภาษา C# ก็เหมือนกับการเขียนโปรแกรมในภาษา .NET อื่นๆ คือไม่มีกฎเกณฑ์ตายตัว สิ่งที่ผู้เขียนจะเรียนให้ทราบต่อไปนี้เป็นเพียงแนวทางปฏิบัติเท่านั้น

 

เลิกใช้ฮังกาเรียนโนเตชันได้แล้ว : ในยุค Win32 นักเขียนโปรแกรมภาษา C++ จะยึดมั่นอยู่กับวิธีตั้งชื่อที่ประดิษฐ์โดยนักเขียนโปรแกรมชาวฮังการีชื่อ ชาลส์ ซิมอะนาย (Charles Simonyi) ซึ่งแนะให้ใส่คำย่อของ data type ไว้นำหน้าชื่อยกตัวอย่างเช่น

  • iBar ตัวแปรชนิดอินทีเจอร์
  • bFoo ตัวแปรชนิดบูลลีน
  • pszOwner พอยน์เตอร์ที่ชี้ไปยัง string ที่ปิดท้ายด้วยเลขศูนย์

วิธีการตั้งชื่อเช่นนี้เรียกว่า Hungarian notation ซึ่งนับว่าเหมาะดีสำหรับการเขียนโปรแกรมในยุค Win32 เพราะทำให้โปรแกรมเมอร์รู้ทันทีว่า object นั้นมี type เป็นอย่างไรเพียงแค่เห็นชื่อ ช่วยลดโอกาสเกิดความขัดแย้งเรื่อง type ระหว่าง object ต่างๆ ลงไปได้

แต่การเขียนโปรแกรมยุค .NET โปรแกรมเมอร์ไม่ต้องกังวลเรื่องความขัดแย้งนี้อีกต่อไป เพราะโปรแกรมใน .NET มีคุณสมบัติ type-safe และสิ่งต่างๆ กลายเป็น object ไปหมด เพียงเลื่อนเมาส์ไปยังชื่อ โปรแกรม Visual Studio ก็จะแสดง type ให้เห็นทันที การตั้งชื่อตามลัทธิ ฮังกาเรียน โนเตชัน จึงไม่จำเป็น และไม่เหมาะสมอีกต่อไป

 

ชาล์ส ซิมอะนาย (Charles Simonyi) ผู้ริเริ่มวิธีฮังกาเรียนโนเตชัน (ภาพนี้ถ่ายในปี 1970 ปัจจุบัน ซิมอะนาย เป็นมหาเศรษฐี เป็นศาสตราจาร์ยมหาวิทยาลัยอ๊อกซ์ฟอร์ด และเจ้าของบริษัทสร้างยานอวกาศเอกชน)

 

แทนที่ด้วย อูฐ ปาสคาลและใหญ่หมด : ทีมผู้สร้าง .NET Framework ส่งเสริมให้นักเขียนโปรแกรมหันมาใช้วิธีตั้งชื่อสามแบบคือ Pascal case และ Camel case และ Upper case

วิธีตั้งชื่อสามแบบนี้เกิดจากข้อจำกัดที่ว่าชื่อต่างๆ ไม่สามารถมี space อยู่เป็นส่วนหนึ่งของชื่อได้ ยกตัวอย่างเช่นท่านต้องการตั้งชื่อตัวแปรไว้เก็บราคาสินค้า ท่านจะตั้งชื่อตัวแปรอย่างนี้ไม่ได้

Product Price หรือ product price

ที่ไม่ได้เพราะระหว่างคำว่า product กับคำว่า price มีตัว space (เคาะวรรค) อยู่ ซึ่งเป็นสิ่งต้องห้าม ท่านจึงจำเป็นจะต้องเขียนติดกันเป็นพรืดไปหมดอย่างนี้

ProductPrice หรือ productprice หรือ Product_price

ท่านคิดว่าอย่างไหนอ่านง่ายที่สุด? เห็นได้ชัดว่าแบบซ้ายสุดอ่านเข้าใจได้ง่ายพอควร แบบตรงกลางอ่านไม่รู้เรื่องเลย และแบบขวาสุดอ่านเข้าใจง่ายดีมากแต่ไม่สวย โปรแกรมเมอร์บางคนชี้ว่ามันน่าเกลียดเพราะดูเหมือนตัวแปรมีรูโหว่อยู่ตรงกลาง

วิธีตั้งชื่อแบบ Pascal case คือวิธีตั้งชื่อตามแบบซ้ายสุดในตัวอย่างข้างบน หลักการก็ง่ายๆ คือให้ตัวอักษรตัวแรกเป็นตัวใหญ่ (upper case) หากเป็นคำที่ประกอบจากหลายคำให้ตัวอักษรแรกของทุกคำเป็นตัวใหญ่ ยกตัวอย่างเช่น AppDomain, ErrorLevel, ReadValue และ ToString เป็นต้น

ไมโครซอฟท์แนะนำให้ใช้วิธีตั้งชื่อแบบ Pascal case นี้กับ type ต่างๆ ต่อไปนี้

Class, Enum, Event, Exception, Read-only Static field, Interface, Method, Namespace, Property และ public instance field

ส่วนการเขียนแบบ Camel case คือการเขียนโดยใช้หลักการเช่นเดียวกับ Pascal case เว้นแต่ว่าตัวอักษรตัวแรกให้เป็นตัวเล็ก (lower case) ดังนั้นหากผู้เขียนต้องการเขียนชื่อตัวแปรสำหรับเก็บราคาสินค้า ProductPrice ให้เป็นแบบ Camel case ก็จะได้เป็นแบบนี้ productPrice ยกตัวอย่างชื่อที่ตั้งโดยวิธี Camel case คือ backColor, frontView, hotFatCat และ scoreInFirstTerm เป็นต้น

ไมโครซอฟท์แนะนำให้ใช้วิธีตั้งชื่อแบบ Camel case นี้กับ type ต่างๆ ดังต่อไปนี้

Field, Parameter, Protected instance field

 

ส่วนการตั้งชื่อแบบ upper case เป็นการใช้ตัวอักษรตัวใหญ่ทั้งหมด แต่แนะนำให้ใช้กับคำย่อเท่านั้น อย่างเช่น System.IO และ System.Web.UI เป็นต้น การตั้งชื่อตัวคงค่า (constant) ให้ใช้แบบ Pascal ไม่ควรใช้ตัวใหญ่ทั้งหมดเช่น PRODUCTPRICE เพราะการใช้ตัวอักษรตัวใหญ่ล้วนๆ บนจอคอมพิวเตอร์ดูขวางหูขวางตา เหมือนมีคนส่งเสียงอื้อฉาว

การตั้งชื่อ object ต่างๆ ในยุค .NET นิยมใช้ชื่อที่สื่อความหมายเต็ม โดยไม่ต้องกังวลเรื่องความยาว ยกตัวอย่างเช่น ท่านต้องการตั้งชื่อฟิลด์ที่ไว้ใช้เก็บที่อยู่ของลูกค้าก็ให้ตั้งว่า customerStreetAddress ไม่ใช่ custStrAdd เพราะชื่อที่ไม่ใช่คำย่ออ่านแล้วเข้าใจได้เร็วกว่าชื่อที่เป็นคำย่อ ท่านไม่ต้องห่วงว่าเวลาเขียนโค้ดจะพิมพ์ยากหรือเสียเวลามาก เพราะ IDE (Integrated Development Environment) สมัยใหม่มีความสามารถสูง ช่วยอำนวยความสะดวกในการป้อนพิมพ์ชื่อยาวๆ ได้เป็นอย่างดี

 

ตอนต่อไป: นิพจน์ในภาษา C#

ตอน 16 : Class


ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาใน Blog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

 

Class

การนิยามคลาสก็คือการสร้าง type ใหม่ การสร้าง type เป็นเรื่องสำคัญที่สุดเรื่องหนึ่งในการเขียนโปรแกรมภาษา C# ภาษาอื่นๆ ที่เป็นภาษา OOP อย่าง C++ C# และ Java ล้วนสนับสนุนให้ผู้นักเขียนโค้ดสร้าง type กันทั้งสิ้น ภาษาที่มิใช่ OOP อย่างภาษา C ไม่มีคลาส มีแต่ struct ซึ่งคล้ายคลาสมาก การสร้าง struc ในภาษา C# ก็ถือเป็นการสร้าง type เช่นเดียวกับคลาส (ดูหัวข้อถัดไปเรื่อง struct)

ดังนั้นการออกแบบและสร้าง type จึงเป็นหน้าที่หลักอย่างหนึ่งของ “กรรมกรซอฟท์แวร์” (software developer)

คลาสคล้าย struct มากจนเกือบเป็นฝาแฝด ข้อแตกต่างที่สำคัญมีเพียงประการเดียวคือ struct เป็น value type ส่วนคลาสเป็น reference type ข้อแตกต่างนี้ทำให้สิ่งที่เราสร้างจากคลาสมีภาวะเป็น object (คือเป็น reference type) ส่วนสิ่งที่เราสร้างจาก struct จะมีภาวะเช่นตัวแปรสามัญ (คือเป็น value type)

หากจะนิยามว่า struct คือกลไกที่เอื้ออำนวยให้นักเขียนโปรแกรมสร้าง data type แบบใหม่ขึ้นได้ เราก็ต้องนิยามว่าคลาสคือกลไกที่เอื้ออำนวยให้นักเขียนโปรแกรมสร้าง object แบบใหม่ขึ้นได้

ในภาษา C# โค้ดสำหรับสร้างคลาสเรียบง่ายมาก ดูตัวอย่าง

class Class1
{
     int foo = 1;
}

โค้ดมีเพียงสามบรรทัดแต่ก็ถือเป็นคลาสที่สมบูรณ์ บรรทัดแรกคำว่า class เป็น keyword ของภาษา C# ทำหน้าที่บอกว่าต่อไปนี้เป็นนิยามของคลาส คำว่า Class1 คือชื่อคลาสที่ผู้เขียนตั้งขึ้นเอง จะตั้งเป็นอย่างอื่นก็ได้ บรรทัดถัดมาเครื่องหมายวงเล็บปีกกาเปิด { ทำหน้าที่บอกให้รู้ว่าไส้ใน (body) ของคลาสจะเริ่มจากตรงนี้ บรรทัดที่สาม int foo = 1; เป็นการสร้างตัวแปรชนิด int หนึ่งตัวและกำหนดค่าเริ่มต้นให้มันด้วย ตัวแปรตัวนี้ถือเป็นตัวแปรที่เป็น “สมาชิก” ของคลาส Class1 คลาสอื่นไม่สามารถเรียกใช้งานได้ เพราะภาษา C# หากไม่ระบุขอบเขต (access modifier) ของสมาชิก ตัวแปลภาษาจะจะกำหนดให้เป็น private โดยอัตโนมัติ ตามปรกติเราจะเรียกตัวแปรที่สมาชิกแบบ private ของคลาสว่า data field

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

  • Constructors
  • Destructors
  • Constants
  • Fields
  • Methods
  • Properties
  • Indexers
  • Operators
  • Events
  • Delegates
  • Classes
  • Interfaces
  • Structs

 

Base class / Derived class

คลาสที่มีคลาสอื่นนำไปใช้สืบสันดานเรียกว่า base class คลาสที่สืบสันดานจากคลาสอื่นเรียกว่า derived class โปรดพิจารณาตัวอย่างโค้ดต่อไปนี้

class TestBase
{
     int foo = 1;
}

class TestDerived : TestBase
{
     int bar = 2;
}

ผู้เขียนสร้างนิยามคลาสสองคลาส คลาสแรกชื่อ TestBase ทำหน้าที่เป็น base class ส่วนคลาส TestDerived ทำหน้าที่เป็น derived class โปรดสังเกตว่า base class ก็เหมือนคลาสธรรมดาทั่วไป ไม่มีอะไรเป็นพิเศษ ส่วน syntax การกำหนด base class ต้องทำตรงส่วนประกาศของ derived class ในตัวอย่างข้างบนคือ TestDerived : TestBase นั่นคือหลังการประกาศชื่อคลาสใส่เครื่องหมาย colon (ทวิภาค : คือเครื่องหมายจุดคู่ที่เห็นทางซ้าย) ตามด้วยชื่อคลาสที่ต้องการนำมาใช้เป็น base class สำหรับคลาสนี้

ในภาษา C# คลาสทุกคลาสเป็น derived class ทั้งนั้น ในตัวอย่างโค้ดข้างบน คลาส TestDerived เป็น derived class เพราะสืบสันดานจาก base class ชื่อ TestBase ส่วนคลาส TestBase เราไม่ได้ระบุ base class C# complier จะถือว่า base class คือ object อันเป็นคลาสที่นิยามไว้ใน namespace System ให้ทำหน้าที่เป็น base class ของคลาสทุกๆ คลาสใน .NET Framework

 

class implicit type conversion

การแปลงชนิดข้อมูลโดยนัย (implicit type conversion) จะเกิดขึ้นระหว่าง base class และ derived class เมื่อนำไปใช้งาน โปรดพิจารณาโค้ดตัวอย่างดังนี้

1 static void Main(string[] args)
2 {
3      TestBase myBase = new TestBase();
4      TestDerived myDerive = new TestDerived();
5      TestBase myConver = new TestDerived();
6      TestDerived myTest = new TestBase();
7 }

โค้ดข้างบนอิงจากคลาสสองคลาส ที่นิยามไว้ในโค้ดตัวอย่างก่อนหน้านี้ โค้ดบรรทัดที่ 3 และ 4 เราสร้าง instance ของ object จากคลาสตรงกับ type ของ object ยกตัวอย่างเช่น บรรทัดที่ 3 เราสร้างตัวแปรชื่อ myBase ให้มี type เป็น TestBase จากนั้นสร้าง instance โดยใช้คำสั่ง new และเรียก constructor ของคลาส TestBase

สิ่งที่น่าสนใจคือบรรทัดที่ 5 เราสร้างตัวแปรชื่อ myConver มี type เป็น TestBase จากนั้นสร้าง instance โดยใช้คำสั่ง new และเรียก constructor ของคลาส TestDerived สามารถทำได้อย่างไม่มีปัญหาด้วยอานิสงส์ของคุณสมบัติ class implicit type conversion ในภาษา C#

โปรดสังเกตว่า implicit type conversion ทำได้จาก base class ไป derived class เท่านั้น ไม่ใช่กลับกัน ในตัวอย่างโค้ดข้างบน คำสั่งบรรทัดที่ 6 เมื่อคอมไพล์แล้วจะเกิด error มีข้อความว่า

Cannot implicitly convert type TestBase to TestDerived

ท่านจะได้เรียนการประยุกต์ใช้งานคลาสโดยละเอียดในภาคสองในหนังสือเล่มนี้

 

หนังสือ The C Programming Language เป็นงานเขียนคลาสสิก เป็นคัมภีร์ภาษาซีฉบับดั้งเดิม เขียนโดยไบรอัน คาร์นิกัน และ แดนนิส ริชชี บิดาแห่งภาษาซี หนังสือเล่มนี้ให้คำจำกัดความของ struc และ union เป็นครั้งแรกในต้นตระกูลภาษาซี

 

stuct

struct คือชนิดข้อมูลที่ผู้ใช้นิยามขึ้นเอง (User defined type) ยกตัวอย่างหากผู้เขียนต้องการเขียน

โปรแกรมเพื่อเก็บข้อมูลสินค้า โดยมี ชื่อสินค้า ขนาด และ ราคาสินค้า ผู้เขียนอาจสร้าง struct ดังต่อไปนี้

struct Product

struct Product
{
     string ProductName;
     int Size;
     double Price;
}

โค้ดข้างบนนี้ทำหน้าที่สร้าง struct ซึ่งมีสมาชิกสามตัว คำสั่งบรรทัดแรกคำว่า struct เป็น keyword ในภาษา C# ทำหน้าที่บอกให้รู้ว่านี่คือนิยามของ struct คำว่า Product เป็นชื่อ struct ซึ่งเป็นชื่อที่ผู้เขียนตั้งขึ้นเอง บรรทัดที่สามผู้เขียนสร้างตัวแปรสมาชิกเป็น string ชื่อ ProductName เพื่อทำหน้าที่เก็บชื่อสินค้า บรรทัดต่อมา int Size ทำหน้าที่สร้างตัวแปรสมาชิกแบบ int เพื่อเก็บขนาดของสินค้า บรรทัดที่ห้า double Price ทำหน้าที่สร้างตัวแปรสมาชิกเพื่อเก็บราคาสินค้า

การจับตัวแปรที่เป็นเรื่องเดียวกันสามตัวมารวมไว้เป็นก้อนเดียวกันอย่างนี้ คำในวิชา OOP เรียกว่าการทำencapsulation เราสามารถสร้าง instance ของ struct ได้โดยใช้คำสั่งดังตัวอย่างนี้

Product A = new Product();

Product B = A;

โค้ดบรรทัดแรกสร้างตัวแปร A ซึ่งมี type เป็น Product (คือ struct ที่นิยามไว้ข้างบน) บรรทัดที่สองสร้างตัวแปรชื่อ B ให้มี type เป็น Product เช่นเดียวกัน และในค่าที่อยู่ภายใน A มาใส่ B ท่านคงสังเกตว่าทั้ง A และ B แม้จะคล้าย object อย่างยิ่งแต่ก็ไม่ใช่ เพราะมันมีต้นกำเนิดมาจาก struct และ struct ทั้งหลายล้วนเป็น value type ซึ่งมีที่พำนักใน stack ทั้งนั้น ซึ่งจะไม่ได้รับการข้องแวะจาก Garbage Collector แต่อย่างใด

ในตัวอย่างข้างบน ทั้ง A และ B เป็นข้อมูลสองชุดต่างหากจากกัน หากเราแก้ไขค่าของ A ค่าของ B จะไม่ได้รับผลกระทบ ในทำนองเดียวกันหากเราแก้ไขค่าของ B ค่าของ A จะไม่เปลี่ยนแปลง

ดูราวกับ struct เป็นเพียงสิ่งที่หลงเหลือมาจากภาษา C เพราะเอกสาร MSDN (Microsoft Development Network) แนะนำให้ทำทุกอย่างด้วยคลาส และใช้ struct เท่าที่จำเป็นจริงๆ เท่านั้น เช่น หาก type ที่ท่านกำลังคิดจะนิยามเป็น type ที่มีสมาชิกน้อย และมีช่วงชีวิตสั้น และมักฝังตัวอยู่ภายใน object ตัวอื่น อาจเป็นการดีหากสร้างมันจาก struct

ทั้งคลาสและ struct เราต้องเป็นผู้ตั้งชื่อมันและสมาชิกทุกตัวของมัน ต่อไปนี้เป็นแนวทางที่จะช่วยให้ท่านตั้งชื่อสิ่งต่างๆ ได้ดีขึ้น

ตอนต่อไป : หลักการตั้งชื่อในภาษา C#