สวัสดีครับ เจอกันอีกครั้งนะครับ ถึงแม้ว่าจะนานไปหน่อย คราวหน้าจะพยายาม update ทุกสัปดาห์นะครับ ยังไงก็ขออภัยที่ทำให้รอคอยนะครับ
วันนี้เรามาต่อกันใน Hello LINQ part 2 ก่อนที่จะเข้าใจเนื้อหานี้ ต้องไปทบทวน/ศึกษา Hello LINQ part1 ก่อนนะครับ ไม่งั้นงงชัวร์
เอาหละครับ เพื่อไม่ให้เสียเวลามาเริ่มเลยครับ
- Extension Methods สำหรับตัวนี้ก็จะทำให้เราเขียนcode ได้อย่างมี styleมากขึ้น(อีกแล้ว) คือต้องเข้าใจนะครับว่า LINQ ส่วนนึงจุดประสงค์เพื่อให้เราทำงานได้ไวและสะดวกขึ้น และ extension methods ก็เป็นเสมือนเครื่องยนต์หลักของ LINQ ทีเดียวครับ ผมจะเปรียบเทียบแบบใช้กับไม่ใช่้ extension methods เพื่อความเข้าใจง่าย
>Student.cs
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public double Cash { get; set; }
}
>StudentTransaction.cs - Method นี้คือ Extension Method
public static class StudentTransaction
{
public static double SumStudentCash(this IEnumerable<Student> students)
{
double sum = 0;
foreach (Student student in students)
sum += student.Cash;
return sum;
}
}
>Student Main Page (in Page_Load) - ใช้เทคนิคประกาศแบบ Collection Initializer ครับ
if (!IsPostBack)
{
var students = new List<Student>() {
new Student { Name = "Sam", ID = 1, Cash = 300.00 },
new Student { Name = "Max", ID = 2, Cash = 500.75 },
new Student { Name = "Eddy", ID = 3, Cash = 100.25 },
new Student { Name = "Simon", ID = 3, Cash = 1200.50 }
};
//old style
resultDiv.InnerHtml = StudentTransaction.SumStudentCash(students).ToString();
//new style with Extension Method
resultDiv.InnerHtml = students.Where(student=>student.Cash>500).SumStudentCash().ToString();
}
แต่ Extension Methods ทำให้คุณสามารถเขียนแบบนี้ได้ลองเปรียบเทียบกับแบบเก่านะครับ
resultDiv.InnerHtml = students.SumStudentCash().ToString();
หรือเราอาจจะใช่ LINQ query operator Where เข้ามาร่วมด้วยก็ยังได้เราครับ
resultDiv.InnerHtml = students.Where(student=>student.Cash>500).SumStudentCash().ToString();
การเขียนcode styleแบบนี้ เรียกว่า Unix-like pipeline ครับ เพราะ เสมือนกับว่าเรา เอาผลลัพธ์ที่ได้จาก query operator Where ไปใส่เป็น input ของ SumStudentCash สุดท้ายก็เอาไปเปลี่ยนเป็น ToString เป็นการประมวลผลลัพธ์โดยป้อน output เป็น input ต่อๆไปเป็น chain เสมือน Unix OS สมัยก่อน
จะเห็นว่าจากตัวอย่างข้างบน ==> class Student ไม่มี SumStudentCash() method แต่ทำไมใช้ได้หว่า.. พอมาดูใน StudentTransaction Class(ซึ่งบังคับว่าต้องเป็น static class ถึงจะใช้ extension methods ได้) ปรากฎว่า มี SumStudentCash() ทีเป็นอย่างนี้เพราะถ้าเราใส่ "this object(ในที่นี้คือ IEnumerable<TResult> เป็น type อื่นก็ได้เช่น int)" ลงไปเป็น input ของ "static" method ใดๆ method นั้นจะถูกเรียกว่า extension methods เสมือนกับว่ามันไป extend class ที่ส่งไปเป็น instance นั่นก็คือ "this IEnumerable<TResult>" แหละครับ เพราะฉะนั้น ตอนนี้เราก็ freedom ที่จะเขียน extension methods ได้อย่างง่ายๆแล้ว ลองดูเลยสิครับ (อย่าลืมว่าต้องเป็นstatic method นะ!)
เสริม เรื่อง query operator Where ครับ ความจริงแล้วพวก query operators ทั้งหลายก็เป็น extension methods ทั้งนั้นครับ รวมไปถึง method อื่นๆ(หลายตัวมากๆ เช่น ToList(), ToString() ทั้งหลาย) ที่เราเห็น icon เป็นรุป
ใน intellisense ของ Visual Studio - สำหรับโครงสร้างคร่าวๆของ Where query operator ก็เป็นดังนี้ครับ (required Lambda Expression + Delegate Skills ไปทบทวนดู part 1 นะครับ แล้วสำหรับ เรื่อง query operators จะอธิบายอย่างละเอียดในบทต่อๆไปนะครับ) :
public static IEnumerable<T> Where<T>(this IEnumerable<T> elements, Func<T,Boolean> filteredMethod)
{
foreach (T element in elements)
{
if (filteredMethod(element))
yield return element;
}
}
สำหรับข้อดีของ Extension Methods :
- Very Readable อ่านเข้าใจง่ายกว่าแบบธรรมดาครับ ลองจินตนาการว่าถ้า เราเขียน students.OrderByStudentID().GetTopTwoStudents().SumTotalCash() อันนี้สมมตินะครับ กับ เราเขียน SumTotalCash(GetTopTwoStudents(OrderByStudentID(students))); อันไหน อ่านง่ายและน่าอ่านกว่ากันครับ
- Intellisense Support อันนี้เด็ดดวงสุดครับ ถ้าแบบเดิมไม่มี intellisense แน่นอน แต่ Extension Methods มีนะลองดูได้เลย ทำให้เราเขียนเร็วแล้วสะดวกขึ้นเยอะ
- Anonymous Types จากตัวอย่าง Student ที่ผ่านมานะครับ ถ้าเราไม่อยากที่จะมานั่งเขียน Student Class หนะครับ เราสามารถทำแบบนี้เลย
if (!IsPostBack)
{
var students = new List<Object>() {
new { Name = "Sam", ID = 1, Cash = 300.00 },
new { Name = "Max", ID = 2, Cash = 500.75 },
new { Name = "Eddy", ID = 3, Cash = 100.25 },
new { Name = "Simon", ID = 3, Cash = 1200.50 }
};
//below syntax error ,I'm idiot! No, I just wanna show you
resultDiv.InnerHtml = students.Where(student => student.Cash > 500).SumStudentCash().ToString();
}
จะเห็นนะครับว่าเราไม่ต้องมี Student Class อีกต่อไป เราใ้ช้ List<Object> และ Anonymous Types แล้ว มีสิ่งที่ควรรู้ไว้คือ Object เป็น type ที่ไม่ Strongly นะครับ แต่ Student เป็น Strongly type และพวก Generic Type ก็นับว่าเป็น Strongly Type ครับ มาถึงขั้นนี้ผมหวังว่าคุณจะเข้าใน Generic Type แล้วหนะครับ ลองย้อนๆไปดูตัวอบ่างครับ IEnumerable<T> Where<T>(...) ===> T ก็คือ Generic Type ครับ (คือเรายังไม่รู้ Type มันจนกระทั้ง Runtime)
แล้วทำไมถึงไม่มี type แบบนี้ได้ไง มันจะwork หรอ ==> work แน่นอนครับ เพราะตอน Compile time ==> Complier จะสร้าง Class นี้ให้เราเก็บเป็นClass Library หรืออะไรก็แล้วแต่ ถ้าสมมติเราลอง reflect มันดูด้วย .NET Reflector จะเห็นว่าเป็นประมานนี้ครับ (สังเกตุว่าจะได้ class ชื่อแปลกๆมา นี่ืคือ Anonymous Type ที่ถูกGenerate ขึ้นอัตโนมัติครับ)

พวก Class ที่ไม่ Strongly Type จะสูญเสียคุณสมบัติความเป็น Class ที่แท้จริง ยกตัวอย่างเช่น students.Where(student => student.Cash > 500).SumStudentCash().ToString(); บรรทัดนี้เราไม่สามารถ ใข้ property ที่เป็นของ student ได้อีกแล้วครับ แล้วมันก็จะฟ้อง error ขึ้นมาว่าไม่มี property ชื่อ "Cash"
แล้ว Anonymous Type จะมีประโยชน์อะไรนอกจากสั้นกว่า มีประโยชน์แน่ครับ สำหรับ LINQ ยกตัวอย่างเช่น ผมต้องการแสดงแค่ Name กัับ ID ของ นักเรียนโดยไม่ต้องการโหลด Cash มาให้เปลืองแรม ผมสามารถ bind() กับ DataGrid แบบนี้ในทันที
if (!IsPostBack)
{
var students = new List<Student>() {
new Student { Name = "Sam", ID = 1, Cash = 300.00 },
new Student { Name = "Max", ID = 2, Cash = 500.75 },
new Student { Name = "Eddy", ID = 3, Cash = 100.25 },
new Student { Name = "Simon", ID = 3, Cash = 1200.50 }
};
GridView1.DataSource = students.Select(student => new { StudentName = student.Name, StudentCash = student.Cash });
GridView1.DataBind();
}
ตรงที่ผม mark bold ไว้คือการประยุกต์ Anonymous Type ของ LINQ อย่างนึงครับ
เอาหละครับ ตอนนี้เราก็มีพื้นฐานสำหรับ LINQ มาพอแล้ว(ควรจะพอนานแล้วแหละ) เราก็มาเริ่มอะไรจริงๆจังๆสักทีครับ T T ที่ผมปูให้แน่นต่อไปจะสบายครับ เห็น Syntax LINQ แบบไหนมาก็ โอ๊ะ หมูๆ :D
LINQ as language extension
ความหมายในที่นี้ก็คือ LINQ สามารถต่อยอดให้กับภาษาที่เราใช้กันทุกวันๆ C# หรือ VB นั้นเองครับผมจะยกตัวอย่างนะครับ ลองจินตนาการว่าคุณอยากจะรับข้อมูล Student ทั้งหมด(อิงจากตัวอย่างเดิม) ให้ query มาจาก database แล้วเอามา แสดงใน DataGridView แล้วให้ Save เป็น xml เก็บไว้เป็น .xml อีกด้วย ลองจินตนาการด้วยการเขียน C# หรือคุณอาจจะใช้ XML Mapping Library เอาก็ได้ครับ จะต้องเขียนกันขนาดไหนเนี่ย! แต่LINQ comes to rescueครับ เสมือนว่าเป็น bridge เชื่อมระหว่างภาษา C# กับสิ่งทั้งหลายเหล่านั้นด้วยการ extend ภาษาที่คุ้นเคยเล็กน้อย แต่ทำสิ่งใหญ่ๆได้หลากหลาย จึงเป็นที่มาของ LINQ as language extension
var students =
from student in stuDB.GetAllStudents()
where student.Cash > 500
orderby student.Name
select new { student.Name,student.Cash };
GridView1.DataSource = students;
GridView1.DataBind();
var studentXml =
new XElement("students",
from student in students
select new XElement("studentName",student.Name),
select new XElement("studentName",student.Name)
)
);
studentXml.Save(@"c:/students.xml")
เนี่ยแหละครับคือผลลัพธ์แบบง่ายๆที่ได้จาก language extesion โดย LINQ
หวังว่าคงชอบบทความนี้กันนะครับ อย่าลืม leave message/feedback to us นะครับ
ผมทราบซึ้งที่มีคนอ่านมาถึงตอนนี้จริงๆครับ *^*
คราวหน้าเราจะมาต่อกันในเรื่องของ ... รวดเดียวเลยครับ อย่าพลาด Hello LINQ part หน้านะครับ+++
- Query Operators
- Query Expressions
- LINQ to SQL
d33f60cc-070c-4604-a954-4755ae8e32cd|2|5.0