จากที่ผ่านมาได้พูดถึงภาพรวมของ Collection Interface ที่ .Net ได้เตรียมไว้ คราวนี้จะมาดูรายละเอียด ของ Interface 3 ตัวคือ IEnumerator, IEnumerable และ ICloneable (ที่จริงจะกล่าวเฉพาะ IEnumerator และ IEnumerable แต่ในตัวอย่างมีการใช้งาน ICloneable ด้วยก็เลยกล่าวเพิ่มไปซะเลย)
IEnumerator เป็น interface พื้นฐานเพื่อให้ class ที่อ้างถึง interface นี้สามารถใช้ foreach ได้ ภายในมีสมาชิกดังนี้
Methods
Methods
- MoveNext ระบุให้เลื่อน element ปัจจุบันไป 1 ช่อง
- Reset สังให้ current กลับไปจุดเริ่มต้น
Properties
- Current ดึง element ปัจจุบันจาก collection
IEnumerable เป็น interface ที่จะทำการส่งคลาส IEnumerator ไปยังคำสั่ง foreach ภายในมีสมาชิกดังนี้
Methods
Methods
- GetEnumerator ส่งค่า enumerator ที่จะส่งไปยัง foreach
ICloneable เป็น interface ที่กำหนดให้คลาสที่ประกาศต้องมีความสามารถในการ clone ตัวมันเอง
Methods
Methods
- Clone ทำการ clone ตัวมันเองแล้วส่งค่านั้นกลับไป
ตัวอย่าง Code:
ตัวอย่างต่อไปนี้จะเป็นการสร้าง single linklist โดยไม่ใช้คลาสในกลุ่ม collection ของ .Net แต่จะกำหนดโครงสร้างทั้งหมดเองตามลักษณะดังนี้|--------------- MyList -----------------|
|---Node---| |---Node---| |---Node---|
|Value|Next|-->|Value|Next|-->|Value|Next|
|---Node---| |---Node---| |---Node---|
|Value|Next|-->|Value|Next|-->|Value|Next|
จากโครงสร้างดังกล่าวจะมีคลาสสองตัวคือ MyList และ Node โดย MyList จะเป็น collection ของ Node ภายใน Node จะมีสมาชิกภายในคือ Value เก็บค่าตัวเลขที่ต้องการบันทึก และ Next เก็บที่อยู่ของ Node ถัดไป
using System;
using System.Collections.Generic;
using System.Collections;
namespace LinkListDemo
{
// ประกาศคลาส Node
public class Node : ICloneable //ประกาศใช้ ICloneable
public int Value;
public Node Next;
public Node()
{
_value = 0;
}
public Node(int nValue)
{
_value = nValue;
}
public override string ToString()
{
return _value.ToString();
}
#region ICloneable Members
public object Clone()
{
return new Node(_value); }
#endregion
}
 // ประกาศคลาส MyList
public class MyList : IEnumerator, IEnumerable, ICloneable
{
private Node _firstNode;
private Node _curNode; private Node _lastNode;
public MyList()
{
_firstNode = new Node(); //ตำแหน่ง 1.1
}
public bool MoveNext()
{
_curNode = _curNode.Next; //ตำแหน่ง 1.2
return !(_curNode == null);
}
public void Reset()
{
_curNode = _firstNode; //ตำแหน่ง 1.3
}
public object Current //ตำแหน่ง 1.4
{
get
{
if (_curNode != null)
{
return _curNode;
}
else {
throw new InvalidOperationException();
}
}
}
public void Add(Node newNode)
{
if (_curNode == null)
{
_firstNode.Next = newNode;
_curNode = newNode;
_lastNode = newNode;
}
else
{
_lastNode.Next = newNode;
_lastNode = newNode;
}
}
public void Add(int newValue)
{
Node newNode = new Node(newValue);
this.Add(newNode);
}
#region IEnumerable Members
public IEnumerator GetEnumerator() //ตำแหน่ง 2
{
return (IEnumerator)Clone();
}
#endregion
#region ICloneable Members
public object Clone()
{
Node tmpNode = _curNode;
MyList newList = new MyList();
_curNode = _firstNode.Next;
while (_curNode != null)
{
newList.Add(_curNode.Value);
_curNode = _curNode.Next;
}
return newList;
}
#endregion
}
class Program //โปรแกรมเรียกใช้ class MyList
{
static void Main(string[] args)
{
MyList myList = new MyList();
for (int cnt = 0; cnt < 10; cnt++)
{
myList.Add(new Node(cnt));
}
foreach (Node node in myList)
{
Console.WriteLine(node);
}
Console.ReadKey();
}
}
ในการวนรอบของ foreach นั้นคำสั่ง foreach จะเรียก GetEnumerable() (ตำแหน่ง 2) เพื่อเรียก object collector ขึ้นมาโดยที่ object collector ที่คืนกลับนั้นคลาสจะต้องประกาศ IEnumerator ไว้ด้วย หลังจาก foreach ได้ object collector จะมีขั้นตอนการดำเนินการดังนี้
- เรียก MoveNext(ตำแหน่ง 1.2) เพื่อเลื่อนตำแหน่งของ element ไปยังจุดถัดไป ถ้าผลของการ MoveNext ส่งกลับมาเป็น true จะทำงานในข้อ 2 ต่อ ถ้าเป็น false จะหยุดการทำงาน
- ส่งค่าใน element ออกไปผ่าน property Current (ตำแหน่ง 1.4)
- วนกลับไปทำข้อ 1 ใหม่
จากขั้นตอนดังกล่าวจะเห็นว่าเมื่อเริ่มต้นนั้นการวนรอบของ foreach นั้นตำแหน่งของ Current นั้นจะต้องชี้ไปยังตำแหน่งก่อนหน้าค่าแรก (ในกรณีที่เป็น array ที่มีตำแหน่งเริ่มต้นที่ array[0] ตำแหน่งของ current จะต้องชี้ที่ตำแหน่ง -1) เนื่องจาก foreach จะเรียกใช้คำสั่ง MoveNext (ตำแหน่ง 1.3) ก่อนแล้วจึงส่ง element ใน collector ออกไป ดังนั้นในตอนที่สร้าง MyList ขึ้นมาจึงได้สร้าง node ใหม่ให้กับ _firstNode ในทันทีเพื่อป้องกันความผิดพลาดที่จะเกิดจากการอ่านข้อมูลจาก object ที่ไม่มีการอ้างถึง
ไม่มีความคิดเห็น:
แสดงความคิดเห็น