ผู้เขียน: 3surasoruch

Input/output with files

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

โปรแกรมคอมพิวเตอร์ส่วนมากทำงานกับไฟล์ ซึ่งไฟล์จะแบ่งออกเป็น 2 ประเภท คือ text file และ binary ไฟล์ ในบางครั้งการเขียนโปรแกรมนั้นเราจำเป็นต้องเก็บข้อมูลในรูปของไฟล์ เช่น การเก็บข้อมูลการตั้งค่า หรือบันทึก log เพื่อนำมาใช้งานในการเปิดโปรแกรมครั้งใหม่ ในบทนี้ เราจะพูดถึงการเขียนโปรแกรมเพื่อทำงานกับ Text file ในภาษา C# ซึ่งเป็นรูปแบบการเก็บไฟล์ที่สะดวกและง่ายในการใช้งาน และสามารถเข้าถึงได้โดยโปรแกรมทุกประเภท

การอ่านค่าจาก text file

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

using System;
using System.IO;

class ReadFromFile
{
    static void Main(string[] args)
    {
        string line;
        StreamReader file = null;

        try {
            file = new StreamReader("c:\\file.txt");

            while ((line = file.ReadLine()) != null)
            {
                Console.WriteLine(line);
            }
        } catch (FileNotFoundException e) {
            Console.WriteLine("Error: File not found.");
        } finally {

            if (file != null)
            {
                file.Close();
            }
        }
    }
}

ในตัวอย่างข้างบนเป็นวิธีที่ง่ายที่สุดในการอ่านไฟล์ด้วยภาษา C# เราใช้คลาส StreamReaderจากเนมสเปซ System.IO; ที่ใช้สำหรับการอ่านไฟล์ เราเปิดไฟล์จาก "c:\\file.txt" ซึ่งเป็นไฟลฺ์ชื่อ file.txt ในไดร์ C

while ((line = file.ReadLine()) != null)
{
    Console.WriteLine(line);
}

ใน While loop เราใช้เมธอด file.ReadLine() วนซ้ำอ่านข้อความจากไฟล์ที่ละบรรทัดมาเก็บไว้ในตัวแปรชั่วคราว line และแสดงผลออกทางหน้าจอ ถ้าหากไฟล์สิ้นสุดเมธอดจะคืนค่า null กลับมา

นอกจากนี้เรายังตรวจจับความผิดพลาดที่อาจจะเกิดขึ้นในกรณีที่ไม่พบไฟล์ด้วย FileNotFoundException ผลลัพธ์ของโปรแกรมขึ้นอยู่กับไฟล์ของคุณ และคุณต้องกำหนดพาทของไฟล์ให้ถูกต้องด้วย การใช้ Exception กับไฟล์นั้นเป็นสิ่งที่จำเป็นในการจัดการกับข้อผิดพลาดที่อาจจะเกิดขึ้นในระหว่างที่ทำงานกับไฟล์

if (file != null)
{
    file.Close();
}

ในการทำงานกับไฟล์ทุกครั้ง เราจำเป็นต้องปิดการเชื่อมต่อของ File stream ทุกครั้งโดยารใช้เมธอด Close() เพื่อทำให้ไฟล์สามารถนำไปใช้งานอย่างอื่นได้ต่อไป

การเขียนลงบน text file

ตัวอย่างต่อไปเป็นการเขียนข้อความลงเป็น text ไฟล์ในภาษา C# เราจะเขียนลงทีละบรรทัดเช่นเดียวกันกับการอ่านไฟล์

using System;
using System.IO;

class WriteToFile
{
    static void Main(string[] args)
    {
        string line;
        StreamWriter file = new StreamWriter("d:\\settings.txt");

        file.WriteLine("Writing to file line 1.");
        file.WriteLine("Writing to file line 2.");
        file.WriteLine("Writing to file line 3.");

        Console.Write("Enter your message: ");
        line = Console.ReadLine();
        file.WriteLine(line);

        file.Close();

        Console.WriteLine("Writing to file succeed.");
    }
}

ในตัวอย่าง เป็นโปรแกรมในการเขียนข้อมูลลงไปบน text file ด้วยคลาส StreamWriter เราเปิดไฟล์ชื่อ settings.txt เพื่อเขียนข้อมูล ถ้าไฟล์ไม่มีอยู่โปรแกรมจะสร้างไฟล์ใหม่ ถ้าหากมีอยู่แล้วโปรแกรมจะลบไฟล์เดิมทิ้งไปและสร้างไฟล์ใหม่เพื่อเขียนข้อมูล

StreamWriter file = new StreamWriter("d:\\settings.txt");

นี่เป็นคำสั่งในการสร้างตัวแปรเพื่อเขียนไฟล์จากคลาส StreamWriter โดยใส่พาธของไฟล์ลงไปในคอนสตรัตเตอร์

file.WriteLine("Writing to file line 1.");
file.WriteLine("Writing to file line 2.");
file.WriteLine("Writing to file line 3.");

เราใช้เมธอด WriteLine() สำหรับเขียนข้อมูลลงไปใน text file ทีละบรรทัด และคุณสามารถใช้เมธอด Write() ได้เช่นกัน ซึ่งในการเขียนนั้นจะไม่ขึ้นบรรทัดใหม่

Enter your message: MarcusCode Tutorial
Writing to file succeed.

นี่เป็นตัวอย่างในการรันโปรแกรม โดยใส่ข้อความว่า “MarcusCode Tutorial” ในบรรทัดสุดท้ายที่รับจากคีย์บอร์ดเพื่อเขียนลงไปในไฟล์ และเราแสดงข้อความว่า “Writing to file succeed”

// settings.txt
Writing to file line 1.
Writing to file line 2.
Writing to file line 3.
MateoCode tutorial

และนี่เป็นผลลัพธ์ของไฟล์ settings.txt ที่ถูกเขียนขึ้น

การเขียนข้อมูลต่อท้ายไฟล์ (File appending)

ในการเก็บข้อมูลกับไฟล์บางรูปแบบนั้นจำเป็นที่จะต้องเขียนข้อมูลลงไปในไฟล์เดิม เราเรียกว่าการเขียนข้อมูลต่อท้ายไฟล์หรือ File appending ต่อไปเป็นตัวอย่างของโปรแกรมในการเขียนข้อมูลต่อท้ายไฟล์ในภาษา C#

using System;
using System.IO;

class ApendingToFile
{
    static void Main(string[] args)
    {
        Console.WriteLine("Enter 1 to create new file");
        Console.WriteLine("Enter 2 to apend to file");
        Console.Write("Your select: ");
        string choice = Console.ReadLine();

        StreamWriter file = null;
        string fileName = "d:\\my_file.txt";

        try {

            if (choice == "1")
            {
                file = new StreamWriter(fileName);
            }
            else
            {
                file = File.AppendText(fileName);
            }

            string line = null;

            do
            {
                Console.Write("Enter your message: ");
                line = Console.ReadLine();
                if (line != "exit")
                {
                    file.WriteLine(line);
                }

            } while (line != "exit");

        } catch (Exception e)
        {
            Console.WriteLine(e.Message);
        } finally {
            if (file != null)
            {
                file.Close();
            }
        }

        file.Close();

    }
}

ในตัวอย่าง นั้นเป็นโปรแกรมเขียนข้อมูลไปใน text file ซึ่งมีสองแบบให้เลือกที่เขียนคือการสร้างไฟล์ใหม่หรือเขียนต่อจากไฟล์เดิม โดยเมื่อผู้ใช้เลือก 1 หมายถึงเขียนไฟล์โดยสร้างขึ้นใหม่ และ 2 หมายถึงให้เขียนต่อไฟล์เดิม และดรารับข้อความที่ต้องการเขียนผ่านทางคีย์บอร์ดจนกว่าจะพิมพ์คำว่า “exit” เพื่อเป็นการจบการเขียนไฟล์

file = File.AppendText(fileName);

นี่เป็นคำสั่งในการเปิดไฟล์เพื่อเขียนต่อท้าย โดยการใช้เมธอด AppendText() จากคลาส File สำหรับสร้างตัวแปรอ่านไฟล์

ในบทนี้ คุณได้เรียนรู้การทำงานกับไฟล์ในภาษา C# โดยการอ่านไฟล์และการเขียนไฟล์ เพื่อเก็บข้อมูลโดยเฉพาะอย่างยิ่งกับ Text file และการเขียนต่อท้ายไฟล์เดิม

ย้อนกลับ

Threads

ในบทนี้ คุณจะได้เรียนรู้เกี่ยวกับ thread และวิธีการใช้งาน thread ในภาษา C#

Thread คืออะไร

Thread เป็นการทำงานของโปรแกรมที่สามารถทำงานแบบ concurrence (แบบขนาน) โดยแต่ละ thread จะทำงานไปพร้อมกัน โดยปกติการทำงานของโปรแกรมนั้นจะทำงานทีละบรรทัด และต้องรอให้บรรทัดก่อนหน้าเสร็จก่อน โปรแกรมจึงจะประมวลผลในบรรทัดต่อไป

การใช้ thread นั้นสามารถทำให้โปรแกรมทำงานพร้อมกันได้ในแต่ละ thread ที่ถูกสร้างขึ้นภายในโปรแกรม ตัวอย่างการใช้งาน มักจะพบในโปรแกรมที่สามารถแบ่งงานกันทำได้ หรือโปรแกรม server และ client โดยที่ server จะสร้าง thread ขึ้นมาเพื่อจัดการกับแต่ละ client ที่เชื่อมต่อเข้ามา และใช้กับการพัฒนาเกม เป็นต้น

การสร้าง Thread

ในภาษา C# มีไลบรารี่ของ thread ที่เราสามารถใช้งาน มาดูตัวอย่างการสร้าง thread

using System;
using System.Threading;

class Threads
{
    static void Main(string[] args)
    {
        Thread t1 = new Thread(new ThreadStart(Thread1));
        Thread t2 = new Thread(new ThreadStart(Thread2));

        t1.Start();
        t2.Start();
    }

    static void Thread1() {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("Thread 1");
        }
    }

    static void Thread2()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("Thread 2");
        }
    }
}

ในตัวอย่าง เราได้สร้าง 2 thread สำหรับการแสดงผลภายใน thread การใช้ thread นั้นจะต้องนำเข้าไลบรารี่ของมันด้วยคำสั่ง using System.Threading; และเราได้สร้างสอง thread ขึ้นมาและคลาสนี้มีพารามิเตอร์คือคลาส ThreadStart ซึ่งมีพารามิเตอร์เป็น delegate สำหรับใส่ชื่อเมธอดเข้าไปเพื่อให้ thread ทำงาน

Thread t1 = new Thread(new ThreadStart(Method1));
Thread t2 = new Thread(new ThreadStart(Method2));

เราเริ่มต้นให้ thread ทำงานโดยการใช้เมธอด Start() และเราสามารถหยุดการทำงานของ thread ได้ถ้าหากต้องการโดยใช้เมธอด Abort()

Thread 1
Thread 1
Thread 1
Thread 2
Thread 2
Thread 2
Thread 2
Thread 2
Thread 1
Thread 1

นี่น่าจะเป็นผลลัพธ์ของโปรแกรม เพราะว่า thread ทำงานแบบขนานกัน ทำให้ผลลัพธ์นั้นไม่เป็นตามลำดับ เมื่อคุณรันโปรแกรมผลลัพธ์อาจจะแตกต่างกันในการรันแต่ละครั้ง

Pass parameters to Thread

ต่อไปเป็นเราจะสร้าง thread ที่สามารถให้เมธอดส่งพารามิเตอร์ได้ โดยที่เราจะส่งพารามิเตอร์เพื่อนำไปใช้ในเมธอดของ thread ดังตัวอย่างข้างล่าง

using System;
using System.Threading;

class ThreadParameter
{
    static void Main(string[] args)
    {
        Thread thread1 = new Thread(Hello);
        thread1.Start("Marcus");

        Parameter param = new Parameter();
        param.name = "Bob";
        param.word = "How are you going?";

        Thread thread2 = new Thread(Question);
        thread2.Start(param);
    }

    static void Hello (object name)
    {
        Console.WriteLine("Hello " + name);
    }

    static void Question (object data) {
        Parameter param = (Parameter) data;
        Console.WriteLine(param.name + ", " + param.word);
    } 

    class Parameter {
        public string name;
        public string word;
    }
}

ในตัวอย่างเราได้สร้าง thread ขึ้นมาสอง thread และในเมธอด Start() เพื่อเริ่มการทำงาน thread เราสามารถส่งพารามิเตอร์ที่เป็นออบเจ็คชนิดใดก็ได้เพียงแค่ 1 ออบเจ็คเท่านั้น

thread1.Start("Marcus");
...
thread2.Start(param);

เหมือนที่คุณเห็น เราสามารถส่งได้เพียงแค่ 1 ออบเจ็คสำหรับเมธอด Start() ถ้าหากต้องการส่งหลายค่าเราต้องส่งเป็นออบเจ็คเหมือนใน thread2 และในเมธอด Hello() และ Question() สามารถมีพารามิเตอร์ได้เป็นแค่ประเภท object

Hello Marcus
Bob, How are you going?

นี่เป็นผลลัพธ์ของโปรแกรม

Multi-threaded example

ต่อไปมาดูตัวอย่างเพิ่มเติมเกี่ยวกับการใช้ thread ในภาษา C# ซึ่งเป็นโปรแกรมในการแสดงค่าของจำนวนเฉพาะตั้งแต่ 1 – n โดยโปรแกรมจะสร้าง thread ตามจำนวน logical processor ของเครื่องของคุณ โดยแต่ละ thread จะช่วยกันในการคำนวณเลขจำนวนเฉพาะและแสดงผลออกทางหน้าจอ

using System;
using System.Threading;

class MultiThreadExample
{
    static int MAX_NUMBER = 100;
    static int proceed_number = 1;

    static void Main(string[] args)
    {
        // Get number of logical processor from CPU
        int NUM_THREAD = Environment.ProcessorCount;
        for (int i = 0; i < NUM_THREAD; i++)
        {
            Thread t = new Thread(delegate() {
                int threadID = (int) AppDomain.GetCurrentThreadId();

                while (proceed_number <= MAX_NUMBER)
                {
                    int n = proceed_number++;
                    if (IsPrime(n))
                    {
                        Console.WriteLine("Thread " + threadID + " -> " + n + " is prime.");
                    }
                    else {
                        Console.WriteLine("Thread " + threadID + " -> " + n + " is not prime.");
                    }
                }
            });
            t.Start();
        }
    }

    static bool IsPrime(int number)
    {
        for (int i = 2; i < number; i++)
        {
            if (number % i == 0 && i != number) return false;
        }
        return true;
    }
}

ในตัวอย่างเป็นการสร้าง thread แบบไดนามิกส์จากจำนวน logical process ของเครื่องคอมพิวเตอร์ที่ใช้รันโปรแกรมนี้

int NUM_THREAD = Environment.ProcessorCount;

และเรายังสร้าง delegate เมธอดซึ่งเป็น anonymous method ในการเป็นพารามิเตอร์ให้ thread ทำงาน นี่ทำให้เราไม่ต้องไปสร้างเมธอดไว้คนละที่

delegate() {
 ...
}

จากโปรแกรมด้านบน เป็นแค่พื้นฐานในการใช้ thread เท่านั้น ในตัวอย่างมันยังไม่ปลอดภัยเพียงพอ เพราะว่า thread ที่สร้างขึ้นมานั้นมีโอกาสที่จะอ่านค่า proceed_number ค่าเดียวกันในช่วงเวลาใดๆ ก็ได้ ดังนั้นคุณต้องทำการควบคุมการเข้าถึงข้อมูลพร้อมกัน (Reach condition) โดยการใช้วิธี Concurrency control ในแบบที่เหมาะสมกับการทำงานของ thread ที่คุณสร้างขึ้น ซึ่งอยู่นอกเหนือเนื้อหาในบทเรียนนี้

ในบทเรียนนี้ คุณได้เรียนรู้วิธีการสร้าง thread ในภาษา C# และการใช้งานในเบื้องต้น

ย้อนกลับ  ไปต่อ

Preprocessor directives

reprocessor directives เป็นคำสั่งของโปรแกรมที่ประมวลผลในตอนที่คอมไพเลอร์เริ่มต้นทำงาน ซึ่งมันทำงานโดย preprocessor และก่อนที่โปรแกรมจะสร้าง executable file ที่ใช้ในการรันโปรแกรม preprocessor directives นั้นเริ่มต้นด้วยเครื่องหมาย hash (#) และมันใช้บรรทัดใหม่ในการสิ้นสุดคำสั่ง

นี่เป็นตารางของ Preprocessor directives ที่มีในภาษา C#

Directives Descriptions
#if ตรวจสอบถ้าหาก symbol ได้ถูกกำหนด
#else คำสั่งถ้าหากเงื่อนไขจาก #if ไม่ได้ถูกกำหนด
#elif ตรวจสอบถ้าหากมีการกำหนด symbol แบบหลายตัวเลือก
#endif ใช้ควบคู่กับ #if เพื่อปิดบล็อคคำสั่ง
#define ใช้เพื่อกำหนด symbol ในโปรแกรม
#undef ใช้เพื่อยกเลิก symbol ในโปรแกรม
#warning ใช้เพื่อให้คอมไพเลอร์สร้างการแจ้งเตือน
#error ใช้เพื่อให้คอมไพเลอร์สร้างข้อผิดพลาด
#line แก้ไขหมายเลขบรรทัดของคอมไพเลอร์
#region ใช้ในการกำหนดขอบเขตหรือส่วนของโค้ด
#endregion ใช้ควบคู่กับ #region เพื่อสิ้นสุดบล็อคคำสั่ง
#pragma ใช้คำสั่งสร้างคำสั่ง preprocessing แบบกำหนดเอง

ในภาษา C# สนับสนุน proprocessor directives รูปแบบต่างๆ เพื่อใช้ในวัตถุประสงค์ที่แตกต่างกันออกไป ต่อไปมาดูตัวอย่างในการใช้งาน

Macro definitions (#define, #undef)

Macro definitions เป็น directive ที่ใช้กำหนดหรือยกเลิก symbol ที่ใช้ในขอบเขตของโปรแกรม โดยมันมีรูปแบบการประกาศดังนี้

#define SYMBOL_NAME
#undef SYMBOL_NAME

ในการประกาศ symbol ใช้ directive #define ตามด้วยชื่อของ symbol ที่ต้องการประกาศ และการยกเลิกใช้ directive #undef ตามด้วยชื่อของ symbol ที่ต้องการยกเลิก

#define DEBUG

using System;
...

ในตัวอย่าง เราได้ประกาศ symbol DEBUG ในการใช้ directive #define และ #undef นั้น คุณจะต้องทำก่อนคำสั่งปกติของโปรแกรมทั้งหมด

Conditional inclusions (#if, #elif , #else and #endif)

Conditional inclusions เป็น directives เพื่อใช้สำหรับตรวจสอบว่า symbol ว่าได้ถูกกำหนดโดย directive #define หรือไม่ ถ้าคำสั่งถูกกำหนด คอมไพเลอร์จะทำงานในบล็อคของ directive #if ที่สิ้นสุดด้วย directive #endif

#if เป็น directive ที่ใช้สำหรับตรวจสอบถ้าหากว่า symbol ได้ถูกกำหนด คอมไพลเลอร์จะประมวลผลในบล็อคของคำสั่งที่จบด้วย directive #endif สำหรับ directive#elif และ #else เป็นทางเลือกในกรณีที่การเปรียบเทียบแบบหลายตัวเลือก มาดูตัวอย่างในการใช้งาน

#define DEBUG

using System;

class PreprocessorDirectives
{
    static void Main(string[] args)
    {
#if DEBUG
        Console.WriteLine("Program run in debug mode.");
#endif
    }
}

ในตัวอย่างด้านบน เราได้ประกาศ symbol DEBUG และใช้ directive #if ในการตรวจสอบ ใน expression #if DEBUG โดยถ้าหาก DEBUG ถูกประกาศ คอมไพลเลอร์จะประมวลผลในบล็อคของ directive #if

ต่อไปมาดูตัวอย่างเพิ่มเติมในการใช้คำสั่งที่เหลือ

#define DEBUG
#define VERSION_2

using System;

class PreprocessorDirectives
{
    static void Main(string[] args)
    {
#if VERSION_1 && DEBUG
        Console.WriteLine("Version 1 and debug mode is enabled.");
#elif VERSION_2 && DEBUG
        Console.WriteLine("Version 2 and debug mode is enabled.");
#else
        Console.WriteLine("Unknown version.");
#endif
    }
}

ในตัวอย่าง เราได้ประกาศสอง symbol คือ DEBUG และ VERSION_2 เราสามารถใช้ตัวดำเนินการตรรกศาสตร์ ! && และ || ในการสร้าง expression ย่อยได้ และเราได้ใช้ directive #elif และ #else ในการตรวจสอบสำหรับการมีหลายตัวเลือกเพื่อให้คอมไพเลอร์ตรวจสอบ

#warning and #error directives

ในภาษา C# ให้เราสามารถสั่งให้คอมไพลเลอร์สร้างการแจ้งเตือนได้โดยใช้ directive #warning และสร้างข้อผิดพลาดโดยใช้ directive #error ซึ่งมีประโยชน์ในการเตือนโปรแกรมเมอร์ในการเขียนโปรแกรม เพื่อบอกหรือเตือนบางอย่าง

ข้างล่างเป็นตัวอย่างในการใช้ #wringing และ #error ในภาษา C#

using System;

class PreprocessorDirectives
{
    static void Main(string[] args)
    {
        Divide(3, 2);
        DeprecatedMethod();
    }
    
    static float Divide (int a, int b)
    {
        #warning Divide two integers may cause of precision loss.
        return a / b;
    }
    static void DeprecatedMethod ()
    {
        #error This method is deprecated.
    }
}

ในตัวอย่าง เราได้ใช้ directive #warning ในการแจ้งเตือนถ้าหากมีการใช้เมธอด Divide()ซึ่งเป็นเมธอดในการหารตัวเลข integer สองจำนวนซึ่งอาจจะทำให้เกิดข้อมูลสูญหาย และ directive #error ในการแจ้งเตือนถ้าหากมีการใช้เมธอดที่ยกเลิกการใช้งานแล้ว สำหรับการแจ้งเตือนที่เกิดโดย #error โปรแกรมจะไม่สามารถรันได้

Error List Windows in Visual Studio

นี่เป็นรูปผลลัพธ์ของ Error List Windows ใน Visual Studio เมื่อเราคอมไพล์โปรแกรม จะเกิดข้อความแจ้งเตือนและผิดพลาดเกิดขึ้น เพราะเราเรียกใช้เมธอดดังกล่าว ในการที่จะคอมไพล์โปรแกรมได้ ต้องนำโค้ดคำสั่งบรรทัดที่เรียกใช้เมธอด DeprecatedMethod() ในเมธอด Main() ออกไป

#region and #endregion directives

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

class RegionExample
{
    #region variables declareations
    int a;
    float b;
    #endregion

    #region methods declaration
    static void Method1 (string s)
    {
    }
    static void Method2()
    {
    }
    #endregion
}

ในตัวอย่าง เราได้สร้างสอง #region ในคลาส RegionExample เพื่อใช้สำหรับแบ่งกลุ่มการประกาศสมาชิกของคลาสที่เป็นตัวแปรและเมธอด โดยในแต่ละบล็อกของ #region จะจบด้วย directive #endregion

ในบทนี้ คุณได้เรียนรู้เกี่ยวกับ preprocessor directives ประเภทต่างๆ ในภาษา C# และประโยชน์ในการนำไปใช้งานกับการเขียนโปรแกรม และคุณทราบว่า directive เหล่านี้ประมวลผลโดย preprocessor ในตอนคอมไพล์โปรแกรม

ย้อนกลับ  ไปต่อ

Generic Collections

ในบทนี้ คุณจะได้รู้จักกับ Collections ในภาษา C# และการใช้งาน Collections ประเภทต่างๆ ที่มีความจำเป็นในสภานะการณ์ที่แตกต่างกัน

Collections (คอลเล็กชัน) หรือ Generic Collections นั้นเป็นไลบรารี่ของภาษา C# ที่มีอยู่แล้ว ซึ่งเราสามารถที่จะนำมาใช้งานได้เลย โดยมันจะอยู่ภายใน Namespace System.Collections.Generic อย่างไรก็ตาม เราจะพูดถึงเฉพาะ Collection ที่มีการใช้บ่อยๆ ที่สุดเท่านั้น โดยรูปแบบการใช้งาน Generic Collections จะเป็นดังนี้

Type<Data_type> collection_name = new Type<Data_type>();

การใช้ List

List (ลิสต์) นั้นเป็นโครงสร้างข้อมูลที่ใช้ในการเก็บข้อมูลแบบเป็นลำดับ โดยมันมาแทน ArrayList ที่ถูกเลิกใช้ไปตั้งแต่ .Net Framework 2.0 สิ่งแตกต่างของมันกับอาเรย์คือ มันสามารถเก็บข้อมูลได้แบบไดนามิกส์ หรือเพิ่มขึ้นเรื่อยๆ โดยที่เราไม่ต้องบอกขนาดล่วงหน้า และเช่นเดียวกัน Collections ทกตัวนั้นเป็นไดนามิกส์ทั้งหมด

ต่อไปมาดูตัวอย่างการใช้ List ในภาษา C#

using System;
using System.Collections.Generic;

namespace CollectionsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> number = new List<int>();

            number.Add(1);
            number.Add(3);
            number.Add(5);
            number.Add(7);
            number.Add(9);

            for (int i = 0; i < number.Count; i++) {
                int n = number.ElementAt<int>(i);
                Console.Write("{0}, ", n);
            }
        }
    }
}

จากตัวอย่าง เราได้สร้าง List ขึ้นมาโดยลิสต์นี้จะใช้เก็บข้อมูลประเภท Integer โดยลิสต์ของเรานั้นจะมีขนาดเป็นแบบไดนามิกส์

List number = new List();

คุณสามารถเพิ่มข้อมูลเข้าไปในลิสต์ได้ไม่จำกัด โดยใช้เมธอด Add() และพารามิเตอร์นั้นเป็นข้อมูลชนิด Integer ที่คุณได้ประกาศไว้ในตอนแรก ในตัวอย่างเราได้เพิ่มเข้าไป 5 หมายเลข

number.Add(1);
number.Add(3);
number.Add(5);
number.Add(7);
number.Add(9);

ต่อมาเป็นการแสดงผลข้อมูลในลิส โดยCount; นั้นเป็นตัวแปรที่ใช้สำหรับรับค่าขนาดของลิสต์มา และเมธอด ElementAt<int>() สำหรับการนำข้อมูลออกจาก List โดยมีพารามิเตอร์เป็น index ของมัน

            for (int i = 0; i < number.Count; i++) {
                int n = number.ElementAt<int>(i);
                Console.Write("{0}, ", n);
            }

ข้างล่างนี้ผมเป็นรันของโปรแกรม ที่แสดงข้อมูลทั้งหมดภายใน List โดยการใช้คำสั่ง for loop ในการแสดงผล

1, 3, 5, 7, 9,

การใช้ Dictionary

Dictionary เป็น Generic ชนิดหนึ่งในภาษา C# ที่เก็บข้อมูลเป็นแบบคูอันดับของ Key และ Value นั่นหมายความว่ามันใช้ Key เป็น index เพื่อนำข้อมูลมาใช้ มันมักจะถูกนำมาใช้ในงานกรณีที่ Key นั้นเป็นข้อมูลชนิดอื่นๆ เช่น String ซึ่งมันรวดเร็วและสะดวก

มาดูตัวอย่างการใช้งาน Dictionary ในภาษา C#

using System;
using System.Collections.Generic;

namespace CollectionExample2
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> country = new Dictionary<string, string>();

            country.Add("us", "United State");
            country.Add("uk", "Ukrain");
            country.Add("be", "Belgium");
            country.Add("gr", "Greece");

            // Get key and value from  KeyValuePair object
            foreach (KeyValuePair<string, string> cnt in country)
            {
                Console.WriteLine("Key = {0}, Value = {1}", cnt.Key, cnt.Value);
            }

            // Direct access the value via key
            country["gr"] = "GREECE";
            Console.WriteLine("\nuk = " + country["uk"]);
            Console.WriteLine("gr = " + country["gr"]);

            // Remove string object via key
            country.Remove("us");

            if (!country.ContainsKey("us"))
            {
                Console.WriteLine("\nKey \"us\" is not found.");
            }

            Console.WriteLine("\nCount = " + country.Count);
        }
    }
}

จากตัวอย่างข้างบน เราได้สร้าง Dictionary ชื่อมีชื่อว่า country โดยมี Key และ Value เป็น string เพื่อสำหรับเก็บข้อมูลตัวย่อและชื่อของประเทศนั้นๆ ด้วยคำสั่ง

Dictionary<string, string> country = new Dictionary<string, string>();

หลังจากนั้นเราใช้ฟังก์ชัน Add(para1, para2) ในการเพิ่มข้อมูล โดยมันจะมีพารามิเตอร์ของตัวคือ Key และ Value ของมัน ซึ่งแตกต่างจาก Generic แบบ List เล็กน้อย และเราได้ใช้ foreach ในการวนอ่านค่าจาก Dictionary โดยอ่านมาเก็บไว้ในออบเจ็บ KeyValuePair ที่ชื่อ cnt และเราสามารถเข้าถึง Key ได้โดย cnt.Key และสำหรับ Value โดย cnt.Valueและแสดงผลมันออกมาทางหน้าจอ

            foreach (KeyValuePair<string, string> cnt in country)
            {
                Console.WriteLine("Key = {0}, Value = {1}", cnt.Key, cnt.Value);
            }

ต่อไปเป็นการเข้าถึงข้อมูลโดย Key โดยจะเข้าโดยชื่อตัวแปรและวงเล็บ [] จากตัวอย่างเราได้เปลี่ยนค่าให้กับสมาชิกที่มี Key เป็น gr ให้เป็นตัวพิมพ์ใหญ่ และแสดงผลมันออกมา

            country["gr"] = "GREECE";
            Console.WriteLine("\nuk = " + country["uk"]);
            Console.WriteLine("gr = " + country["gr"]);

ต่อไปเราได้ใช้เมธอด Remove() เพื่อลบ Key “us” ออกจาก Dictionary โดยใส่ Key ที่ต้องการลบเป็นพารามิเตอร์ และเราใช้เมธอด ContainsKey() เพื่อตรวจสอบว่ามี Key นี้อยู่หรือไม่ ถ้าไม่มีจะแสดงผลว่า ไม่พบ Key

เนื่องจากมันเป็น Generic ที่ Implement มาจากคลาสเดียวกันคุณสามารถใช้ตัวแปร Countเพื่อดูจำนวนใน Generic ชนิดต่างๆ ได้เสมอ และนี่เป็นผลลัพธ์เมื่อรันโปรแกรม

Key = us, Value = United State
Key = uk, Value = Ukrain
Key = be, Value = Belgium
Key = gr, Value = Greece

uk = Ukrain
gr = GREECE

Key "us" is not found.

Count = 3

มันยังมีเมธอดต่างๆ อีกมากมายที่คุณสามารถใช้ได้สำหรับ Generic แต่ละชนิดคุณสามารถตรวจสอบได้โดย Intellisense (code suggestion) ของโปรแกรม

Generic class ประเภทอื่นๆ

ในภาษา C# ยังมี Generic Collection แบบอื่นๆ อีกที่คุณสามารภใช้ได้ ตามรูปแบบของโปรแกรมของคุณ เช่น Stack เป็นออบเจ็คข้อมูลแบบสมาชิกที่เข้าทีหลังจะออกมาก่อน หรือ Queue สมาชิกที่เข้าก่อนจะออกจาก เป็นต้น

ในบทนี้คุณได้รู้จักกับ Generic พื้นฐานในภาษา C# และการใช้งานแบบต่างๆ และคุณสามารถดู Generic ทั้งหมดได้ที่นี่ https://msdn.microsoft.com/en-us/library/system.collections.generic(v=vs.110).aspx

ย้อนกลับ  ไปต่อ