LINQ นั้น ย่อมาจาก Language Integrated Query หรือแปลเป็นไทย ก็ "การทำ Query แบบฝังในภาษา" ซึ่งในความเป็นจริงนั้นก็ไม่ได้ต่างจากคำแปลซักเท่าไหร่นักครับ เพราะถ้าโค๊ดที่มีการใช้ LINQ คุณจะพบกับคำสั่ง ที่มีลักษณะคล้ายภาษา SQL แบบนี้ ในโค๊ดเลย
คุณอาจจะมองว่า เฮ้ นี่ต่อไปเราจะไม่ต้องมานั่งต่อฐานข้อมูล หรือจำว่าต้องสร้าง Connection แล้วสั่ง New Command จากนั้น ExecuteReader กันแล้วใช่มั๊ย? นั่นก็ถูกต้องครับ แต่ไม่ซะทีเดียว เพราะอันที่จริงแล้ว ถ้าเรามองจาก Syntax และเทคนิคที่ใช้ LINQ นั้น ถูกออกแบบมาให้ทำงานกับข้อมูลในแรมครับ แต่การที่เราสามารถใช้ LINQ ไปทำการติดต่อกับฐานข้อมูลได้เหมือนกัน แต่ว่า เดี๋ยวเราจะกลับมาในเรื่องนั้นอีกครั้ง ภายหลัง
ถ้าไม่ต้อฐานข้อมูล แล้วจะอ่านไปเพื่ออะไร?
เนื่องจากการจะเข้าใจ LINQ แบบถึงแก่น และจะได้นำเอามันไปใช้ให้เหมาะสม รวมถึงเอาฟีเจอร์ใหม่ๆ ที่เป็นผลพลอยได้จากการมี LINQ ไปใช้ได้ด้วย บทความในชุดนี้ผมจะพยายามครอบคลุมเรื่องพื้นฐานทั้งหมดด้วย สำหรับผู้ที่เริ่มจะเข้ามาใช้ C# และเริ่มคุ้นเคยกับภาษาแล้ว แต่ยังไม่ได้ลองเล่นกระบวรท่าพิสดารของภาษานี้ จะได้ฝึกวิทยายุทธตามไปด้วยครับ ส่วนผู้ที่เป็นอยู่แล้ว ก็น่าจะได้มาช่วยกันขัดเกลาให้มันดียิ่งขึ้น ในเรื่องที่เป็นอยู่แล้ว และมาศึกษาฟีเจอร์ใหม่ๆ ที่เพิ่มเข้ามา จะได้นำเอามันไปใช้ได้ และทำให้เขียนโค๊ดได้เร็ว และกระชับมากขึ้นครับ
แล้วถ้าจะใช้ LINQ กับข้อมูลในแรม มันจะมีประโยชน์อะไร???
มีแน่นอนครับ ผมเชื่อว่าถ้าคุณเขียนโปรแกรมที่ต้องมีการแสดงผลข้อมูลจำนวนมากคุณจะต้องได้ทำสิ่งเหล่านี้แน่นอน
- Sort (เรียง) ข้อมูล ตามที่ผู้ใช้กำหนด
- Filter ข้อมูล ตามที่ผู้ใช้กำหนด
- หาค่าต่างๆ เช่น Min, Max, Mean, Sum
- จัดกลุ่มข้อมูล
แต่คุณก็จะคิดในใจว่า อ้าว พวกนี้ เราก็ใช้ตัวฐานข้อมูล ทำให้เราได้แล้วนี่นา? ถ้าเราอยากจะเรียง เราก็สั่งให้ Sql Server มัน ORDER BY ให้เรา หรือว่า จะ Filter เราก็ใช้ WHERE หาค่า MIN, MAX, SUM ก็ใช้ SELECT SUM(field) FROM table หรือจะจัดกลุ่ม ก็ GROUP BY ผมก็บอกว่า นั่นก็ถูกต้องครับ แต่ถ้าลองนึกดู จุดที่ LINQ จะมีประโยชน์ ก็คือ
- แหล่งข้อมูล อาจไม่ได้มาจากฐานข้อมูลเสมอไป
อย่างในปัจจุบัน ที่เราพยายามทำอะไรเป็น SOA มากขึ้น เราอาจจะทำการเรียกข้อมูล จาก Web Service มาก็ได้ ซึ่งข้อมูลที่ได้มา ก็จะเป็นในรูปของ Array ของคลาส หรือว่า เป็น List<คลาส> แบบนี้ แน่นอนว่า เราจะหวังพึ่งคำสั่ง SQL ไม่ได้แน่นอน หรือไม่อย่างนั้น เราก็อาจจะต้องทำ Web Service ที่เปิดพารามิเตอร์ รับเป็นคำสั่ง SQL ซึ่งนอกจากจะมีความเสี่ยงต่อ SQL Injection Attack แล้ว ก็ยังมีปัญหาที่...
- ช่วยปริมาณและความถี่ของการเปิด Connection ไปยังแหล่งข้อมูล หรือตัว Web Service
ถ้าหากว่า เราจะใช้การติดต่อฐานข้อมูล หรือ Web Service ทุกครั้ง ที่มีการเปลี่ยนแปลงการ Sort, Filter แน่นอนว่า ก็ต้องมีการเปิด Connection ไปยังฐานข้อมูล หรือ Web Service ทุกครั้ง ใช่ไหมครับ? นอกจากจะเป็นการทำให้โปรแกรมเราดูช้าลงแล้ว (เพราะต้องรอผลจาก Server) ยังเป็นการไปเพิ่ม Load ให้กับ Server เหล่านั้นด้วย ลองนึกถึงว่า ถ้าโปรแกรมเราทำงานที่ธนาคาร แล้วผู้บริหารระดับสูงต้องการดูรายงาน Transaction ที่มียอดสูงเกิน 1 ล้านบาท ที่เกิดขึ้นในแต่ละวัน จากผู้ลูกค้าจำนวนกว่า 10 ล้านคนทุกวัน แล้วให้เขาสามารถเล่นกับข้อมูลได้ เอาแค่โดยการ Sort ก็พอ ลองคิดดูสิครับ ว่า Transaction ที่เกิน 1 ล้านบาท อาจจะมีแค่ราว 1,000 Transaction แต่ระบบฐานข้อมูล จะต้องไปทำการ Query ข้อมูล 1,000 แถวนี้มาจากตาราง ที่อาจจะมีข้อมูลถึ 5 ล้านแถวต่อวัน แล้วส่งกลับมาให้ แน่นอนว่า โปรแกรมเราคงจะค้างไปนาน กว่าจะได้ผลลัพธ์ แน่นอนว่า ทางที่ดีที่สุด เราก็แค่เขียนฟังก์ชั่น Quick Sort หรือใช้ Array.Sort กับข้อมูล 1,000 แถว ที่อยู่ในโปรแกรมเราอยู่แล้ว น่าจะดีกว่า ถูกไหมครับ?
เอาละครับ เรารู้ถึง ประโยชน์ของมันแล้ว ลองมาทำความรู้จักกับมันแบบลึกซึ้งกันดีกว่าครับ
LINQ คือ แนวคิดแบบ Functional Programming
ถึงตรงนี้ อย่าเพิ่งไปเปิดอ่าน Text เรื่อง Functional Programming (FP) นะครับ ผมเองก็ยอมรับว่า เคยพลาดไปพยายามทำความเข้าใจอยู่พักใหญ่ แล้วยังไปแนะนำให้คนอื่นลองหาอ่านอีก (แหะๆ) เพราะว่า แนวคิดนี้ ไม่ใช่ความคิดใหม่ครับ เป็นแนวความคิดที่เกิดขึ้นเมื่อกว่า 30 ปีที่แล้ว เป็นมีภาษาที่ชื่อว่า ML ที่เป็นแนวคิดนี้ด้วยครับ จะไปลองเล่นดูก็ได้นะครับ
และเหมือนว่า ทุกคน (หมายถึง พวกนัก Com Sci ทั้งหลาย) จะรู้แล้วว่า แนวคิดนี้ ใช้แบบเพียวๆ เลย ไม่ดี จะต้องใช้ร่วมกับภาษาโปรแกรมมิ่งทั่วๆ ไป ที่เราใช้กันอยู่ ถึงจะได้ประโยชน์สูงสุด ซึ่ง LINQ เอง ก็คือการนำเอาแนวคิดนี้มาประบุกต์ใช้ครับ
ลองดูโค๊ด LINQ ด้านล่างนี้ ซึ่งคือการเลือกเอาตัวเลข ที่มีค่าน้อยกว่า 5 ออกมาทำการยกกำลังสอง ครับ
เชื่อหรือไม่ครับ ว่า อันที่จริงแล้ว ในตัว CLR หรือ ตัว .NET Runtime นั้น ไม่ได้มีการแก้ไข เพื่อให้รองรับกับ LINQ เลย เพราะว่า IL (โค๊ดที่ .NET ใช้ในการรันโปรแกรม) ที่สร้างขึ้น ยังคงเป็น IL ที่รันบน CLR เวอร์ชั่น 2.0 ได้โดยสมบูรณ์ เพราะว่าคำสั่ง LINQ นั้น ได้ถูกแปลง ให้กลายเป็นแบบนี้ครับ
นั่นก็คือ เราเรียกใช้ฟังก์ชั่นของ Array (ตัวแปร list) ที่ชื่อ Where แล้วก็ได้เป็นค่า ค่าหนึ่งออกมา จากนั้น ก็เรียกฟังก์ชั่น Select ของค่า ค่านั้นอีก และสังเกตว่า พารามิเตอร์ของฟังก์ชั่นทั้งสอง มันไม่ได้เป็นตัวแปรธรรมดา อ่านแล้วดูงงๆ ลักษณะมันเหมือนกับฟังก์ชั่นเลยทีเดียว และนี่แหละครับ คือตรงที่ผมบอกว่า LINQ คือ การนำเอาข้อดีของแนวคิด FP ซึ่งมีมาเมื่อกว่า 30 ปีที่แล้ว มาใช้ ในภาษายุคใหม่ นั่นเอง ถ้าอ่านแล้วไม่เข้าใจ ก็ผ่านไปได้ครับ มันเป็นทางหนึ่งที่เขียนได้ แต่ไม่จำเป็นจะต้องเขียนแบบนั้นครับ ให้ Compiler แปลงให้เราดีกว่า
อันที่จริงแล้วแนวคิดแบบ FP นี่ มีตั้งแต่ใน .NET เวอร์ชั่นแรกๆ ครับ มันคือการที่เราสามารถส่ง Function ให้เป็น พารามิเตอร์ของอีกฟังก์ชั่นหนึ่งได้ นั่นเอง สมมุติว่า ผมมีฟังก์ชั่นนี้นะครับ
1: /// <summary>
2: /// Compares a to b
3: /// </summary>
4: /// <param name="a"></param>
5: /// <param name="b"></param>
6: /// <returns>true if a is less than b</returns>
7: static bool Compare(int a, int b)
8: { 9: return a < b;
10: }
11:
12: static void Main(string[] args)
13: { 14: Console.WriteLine( Program.Compare( 1, 2 ) );
15: }
เมื่อผมรันโปรแกรม ผมก็จะได้ผลลัพธ์เป็น true แน่นอน เพราะว่า 1 นั้น น้อยกว่า 2 เป็นการเปรียบเทียบแบบ จากน้อยไปมาก (คือ ต้องใส่ตัวที่มีค่าน้อยกว่า เป็นพารามิเตอร์ตัวแรก จึงจะได้ผลเป็นจริง) ...แล้วถ้าสมมุติว่า ผมต้องการให้คนที่เรียกฟังก์ชั่น Compare นี้ ระบุได้ว่า จะให้ฟังก์ชั่นนั้น ทำการ Compare แบบมากไปน้อย หรือน้อยไปมาก จะทำอย่างไรดีครับ? ก็ต้องรับพารามิเตอร์เพิ่ม ในลักษณะนี้
1: /// <summary>
2: /// Compares a to b
3: /// </summary>
4: /// <param name="a"></param>
5: /// <param name="b"></param>
6: /// <returns>true if a is less than b and isDescending is false</returns>
7: static bool Compare(int a, int b, bool isDescending)
8: { 9: if (isDescending)
10: { 11: return a > b;
12: }
13: return a < b;
14: }
นั่นก็คือ มีพารามิเตอร์เพิ่ม อีกเพื่อระบุว่า จะให้เรียงแบบไหน แต่ถ้าเป็น FP เราจะให้ผู้ที่เรียกฟังก์ชั่น ส่งวิธีการเปรียบเทียบมาให้เรา แทนที่จะบอกว่า เราจะเปรียบเทียบอย่างไร แน่นอนว่า ส่วนที่เป็นการระบุวิธีที่ว่า ในการเขียนโปรแกรม ก็คือ ฟังก์ชั่น นั่นเอง แล้วที่นี้ เราจะส่งฟังก์ชั่น ไปเป็นพารามิเตอร์ได้อย่างไร?
ตัวแทนของฟังก์ชั่น (Delegate)
ใน .NET มีสิ่งที่ทำหน้าที่เป็น ตัวแทน ของฟังก์ชั่น ชื่อแปลตรงตัวเลยว่า Delegate ครับ Delegate มีหน้าตาแบบนี้ครับ
delegate bool Comparer(int a, int b);
จะเห็นว่า หน้าตาของมัน เหมือนกับการประกาศฟังก์ชั่น เพียงแต่มีคีย์เวิร์ดว่า delegate นำหน้า เท่านั้นเอง ซึ่ง delegate ที่ชื่อ Comparere นั้น จะสามารถทำหน้าที่เป็นตัวแทนของฟังก์ชั่น ที่มีหน้าตาเหมือนกับตัวมันได้ครับ นั่นก็คือ ฟังก์ชั่น ในลักษณะนี้
bool MyComparer(int a, int b)
{
return true;
}
และถ้าเราต้องการสร้างตัวแทน ของฟังก์ชั่น MyComparer ขึ้นมา ก็ทำได้ในลักษณะเดียวกับการประกาศตัวแปรทั่วไปครับ และให้สังเกตุที่ Intellisense ของตัว Visual Studio จะเห็นว่า มันระบุชัดเจนว่า พารามิเตอร์ ที่จะส่งให้ Constructor ของ Delegate นั้น จะต้องเป็นฟังก์ชั่นในลักษณะ bool (int, int) หรือ ฟังก์ชั่นใดๆ ก็ตามแต่ ที่ คืนค่าเป็น bool และรับพารามิเตอร์สองตัว เป็น int ทั้งคู่
และหลังจากมี delegateToMyComparer แล้ว เราก็สามารถใช้ตัวแทนตัวนี้ เรียกฟังก์ชั่นได้แล้ว
จะเห็นว่า ตอนนี้เราก็มีทุกอย่างพร้อมแล้ว ที่สะส่งฟังก์ชั่น ไปเป็นพารามิเตอร์ของฟังก์ชั่น Compare ซึ่งเราอาจจะแก้ไขโค๊ดของฟังก์ชั่น Comparer ให้เป็นดังนี้
/// <summary>
/// Compares a to b
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
static bool Compare(int a, int b, Comparer f)
{
return f(a, b);
}
และถ้าผมต้องการเรียกใช้ฟังก์ชั่น Compare ผมก็จะต้องส่งฟังก์ชั่น ที่จะใช้ในการเปรียบเทียบค่าไปด้วย ดังนี้
1: delegate bool Comparer(int a, int b);
2:
3: /// <summary>
4: /// Compares a to b
5: /// </summary>
6: /// <param name="a"></param>
7: /// <param name="b"></param>
8: /// <returns>true if a is less than b and isDescending is false</returns>
9: static bool Compare(int a, int b, Comparer f)
10: { 11: return f(a, b);
12: }
13:
14: static bool Ascending(int a, int b)
15: { 16: return a < b;
17: }
18:
19: static bool Descending(int a, int b)
20: { 21: return a > b;
22: }
23:
24: static void Main(string[] args)
25: { 26: Comparer asc = new Comparer(Program.Ascending);
27: Comparer desc = new Comparer(Program.Descending);
28:
29: bool result = Program.Compare(1, 2, asc);
30: bool result2 = Program.Compare(1, 2, desc);
31: }
ถ้าทดลองรันโปรแกรมดู ก็จะได้ผลดังนี้
ผู้อ่านที่คิดตามผมไปด้วย อาจจะนึกได้ว่า อ้าว แล้วจะทำแบบนี้ไปทำไม? ทำไมไม่ไปเรียกฟังก์ชั่น Ascending, Descending เสียเลย จะต้องมาทำเป็น Delegate ทำไมให้วุ่นวาย? เพราะว่านั่นเป็นตัวอย่างครับ
ลองนึกถึงกรณีที่ถ้าหากว่าคุณจะต้องเขียนฟังก์ชั่น Quick Sort เอง แล้วอยากจะให้คนเรียก สามารถกำหนดว่าจะให้เรียงจากน้อยไปมาก หรือมากไปน้อย หรือในกรณีที่สิ่งที่เราจะ Sort เป็นคลาสของนักเรียน แล้วเราต้องการจะ Sort โดยเรียงจาก เลขประจำตัว ชื่อ นามสกุล แบบนี้ แน่นอนว่า คงจะไม่มีทางอื่น นอกจาก รับ "วิธีเปรียบเทียบ" มาจากผู้ที่เรียกฟังก์ชั่น ถูกไหมครับ เพราะเราไม่มีทางรู้ได้เลยว่า เราจะ Sort อะไร ยกเว้นแต่ว่า เราจะต้องมีฟังก์ชั่น Sort สำหรับ นักเรียน คุณครู รถ เรือ บ้าน แยกกันทั้งหมด 
มีฟังก์ชั่นจำนวนมากใน .NET ที่รับ Delegate เป็นพารามิเตอร์ ตัวอย่างหนึ่งก็คือ Array.Sort ซึ่งก็คืออัลกอริธึม QuickSort ที่สามารถ Sort อะไรก็ได้ ไม่ว่าจะเป็น int, long หรือ นักเรียน แบบนี้ครับ
ทำความรู้จักกับฟังก์ชั่นให้ดีขึ้นอีกหน่อย
ลองย้อนไปตอนสมัยเรียนมัธยมนะครับ ผมว่า เราน่าจะจำเรื่องเซ็ต ความสัมพันธ์ และ ฟังก์ชั่นกันได้ ถ้านึกไม่ออก มันคือรูปทางด้านซ้ายมือนี่ละครับ รูปนี้บอกว่า ฟังก์ชั่นคือ ความสัมพันธ์แบบพิเศษระหว่างเซ็ตสองเซ็ต นั่นก็คือ เซ็ตทางขวามือ เรียกว่า โดเมน และเซ็ตทางซ้ายมือ เรียกว่า เรนจ์ หรือถ้าในภาษา Com Sci โดเมนก็คือ พารามิเตอร์ และ เรนจ์ คือ ค่าที่ฟังก์ชั่นนั้น คืนค่าออกมานั่นเอง
ซึ่งในทางคณิตศาสตร์ เราอาจเขียนฟังก์ชั่นได้ แบบนี้ (ภาพจาก Wikipedia)

อ่านได้ว่า f คือ ฟังก์ชั่น จากจำนวนธรรมชาติ (N = Natural Number) ไปยัง จำนวนจริง (R = Real Number) โดย n ไปยัง n หาร ค่าพาย
และนั่นก็คือที่มาของฟีเจอร์แรกที่เพิ่มเข้ามาใน C# 3.0 เพื่อสนับสนุน LINQ ครับ
Lambda Expression
หลังจากเรานึกกันออกแล้วว่า ฟังก์ชั่น มันหน้าตาเป็นยังไง เราก็สามารถทำให้การเขียนฟังก์ชั่นสั้นๆ ทางคณิตศาสตร์ สั้นลงได้ อย่างในกรณีของการส่งฟังก์ชั่นไปเป็นพารามิเตอร์ของฟังก์ชั่น Compare ที่ผ่านมา เราอาจจะไม่จำเป็นที่จะต้องสร้างฟังก์ชั่นขึ้นมาเป็นทางการขนาดในตัวอย่างที่ผมให้ไว้ก็ได้ แต่สามารถเขียนให้มันสั้นลงได้แบบนี้
นั่นก็คือ แทนที่ผมจะสร้างฟังก์ชั่นขึ้นมาใหม่ เพื่อใช้ในการส่งค่าให้กับฟังก์ชั่นนี้โดยเฉพาะ ผมอาจจะใช้การเขียนฟังก์ชั่น ในรูปแบบ Lambda Expression เพื่อความสะดวกได้ครับ ถ้าท่านที่สงสัยอยู่ว่า มันคือ Anonymous Method ใช่หรือไม่? ถูกต้องแล้วครับ มันคือ Syntactic Sugar ที่ช่วยให้คุณเขียน Anonymous Method ได้ง่ายขึ้นนั่นเอง จะเห็นว่า การเขียนทั้งสองแบบ ให้ผลเหมือนกัน
สำหรับการเขียนแบบ Lambda Expression ก็คือ การเขียนฟังก์ชั่น ในลักษณะเดียวกับฟังก์ชั่นในคณิตศาสตร์ ครับ เปรียบเทียบกันก็คือ ถ้าคุณต้องการเขียนฟังก์ชั่นนี้

ในแบบ Lambda Expression ก็คือ (n) => n / Math.PI นั่นเอง หรือจะให้ดูเป็นทางการหน่อย ก็คือ (int n) => n / Math.PI
สำหรับ Anonymous Method นั่น โปรแกรมเมอร์จะมองออกเป็นรูปแบบของฟังก์ชั่นได้ชัดเจนกว่า แต่แทนที่จะสร้างฟังก์ชั่น ที่มีชื่อ เราก็สร้างเป็นฟังก์ชั่นไม่มีชื่อขึ้นมาแทน โดยลักษณะการเขียน ยังคงเหมือนกับฟังก์ชั่นทั่วไปทุกประการ ลองหาอ่านเพิ่มเติมได้ครับ
ทีนี้ แล้วคุณสงสัยบ้างไหมครับว่า ในเมื่อภาษา C# เป็นภาษาแบบ Strongly-Typed หรือ ภาษาที่เข้มงวด และจะโวยวายเมื่อเราทำอะไรผิด Type อย่างเอา double ไปใส่ int แล้วทำไม มันถึงยอมให้เรามี Lambda Expressionได้ โดยไม่ต้องกำหนด type อะไรเลย???
ความสามารถในการอนุมานชนิดตัวแปร (Local Type Inference)
ลองสังเกตตัวอย่างนี้ครับ
แน่นอนว่า ไม่ใช่ว่าภาษา C# 3.0 นี้ จะโดนเปลี่ยนเป็นภาษาแบบ VB6 ที่ไม่สนใจ Type นะครับ ถ้าลองรันดูจะพบว่าอันที่จริงแล้ว result นั้น ก็มี Type เหมือนกัน
แต่ว่า เราไม่จำเป็นต้องระบุ Type แต่ว่า Compiler สามารถระบุ Type ให้เราได้ โดยอัตโนมัตินั่นเองครับ โดยการวิเคราห์จากค่าที่ใช้ในการ Assign ให้กับตัวแปร นั่นหมายความว่า...
ถ้าคุณพยายามใช้ var กับตัวแปร แต่ไม่ได้ทำการใส่ค่าให้กับมัน Compiler ก็จะโวยวาย เพราะมันไม่สามารถระบุ Type ให้คุณได้ นั่นเองครับ แล้วในเมื่อภาษา C# ก็ยังคงเป็น Strongly-Typed เหมือนเดิม แล้วจะมีความสามารถในการระบุ Type นี้ไว้ทำไมกัน? นั่นก็เพราะว่า คุณสามารถทำแบบนี้ได้ครับ
Projection และ Anonymous Type
ในการแสดงผลข้อมูล บางครั้ง ข้อมูลที่เราต้องการแสดงผล อาจจะมีมิติมากจนเกินไป ซึ่งทำให้คนที่จะเป็นผู้บริโภคข้อมูลนั้น รับรู้ได้อย่างลำบาก หรือแม้แต่เกินขีดความสามารถในการแสดงผลไปเลยก็เป็นได้ เราจึงจำเป็นต้อง Project ข้อมูลเฉพาะที่เราต้องการจริงๆ หรือจะเป็นประโยชน์จริงๆ ออกมา
อันที่จริงแล้ว คุณอาจจะนึกไม่ออกว่า ตอนไหนกันที่เราจำเป็นจะต้อง "ย่อมิติ" ของของมูล? แต่ทุกครั้งที่คุณเปิดเกม 3 มิติขึ้นมาเล่น นั่นแหละครับ คือผลจากการ Projection เพราะข้อมูลที่อยู่ในเกม เป็นข้อมูล 3 มิติ ซึ่งมีด้านลึกด้วย แต่ว่าจอภาพของเรา เป็นจอภาพแบบ 2 มิติ ซึ่งแสดงผลแนวลึกไม่ได้ จึงต้องมีการ Project ข้อมูลที่เป็นโพลีกอน (วิ่งเตะบอลกันอยู่นั่น) ออกมาเป็นภาพ 2 มิติ เพื่อแสดงผล
ทีนี้ กลับมาที่การ Projection ของเราบ้าง จะเห็นว่า ในคลาส Student ของเรา มีข้อมูลอยู่ด้วยกันหลายมิติ นั่นก็คือ ID, Name และ Surname แต่ว่า ในการแสดงผล ถ้าเราต้องการแสดงผล Student ในรูปแบบตาราง ที่มีคอลัมน์ StudentID และ FullName เราก็จะต้องทำการ Project เฉพาะมิติที่เราต้องการออกมา ตาม แต่ปัญหาอยู่ที่ การจะเก็บข้อมูนนั้น จำเป็นจะต้องมีคลาส แล้วแบบนี้เราไม่ต้องสร้างคลาสสำหรับการ Projection ทุกแบบ ที่คุณต้องการอย่างนั้นเหรอครับ?
แน่นอนว่า ไม่ต้องครับ เนื่องจากคุณสามารถสร้างคลาส "นิรนาม" ออกมาได้ ทันทีที่คุณต้องการ อย่างเช่นตัวอย่างนี้ครับ
ซึ่งการสร้างคลาสนิรนามนี้ จะใช้ Syntax แบบ Object Initializer ที่เพิ่มเข้ามาใน C# 3.0 ครับ ถ้ายังนึกไม่ออกว่ามันจะใช้งานยังไง ลองดูตัวอย่างนี้ครับ

นั่นก็คือ คุณสามารถ ทำการเซ็ทค่า Property ต่างๆ ได้ทันที ในบรรทัดเดียว ซึ่งจะทำให้การเซ็ทค่าต่างๆ ดูเป็นระเบียบมากขึ้น และทำให้เราสามารถใช้ Projection ได้อย่างสะดวกยิ่งขึ้น ในคำสั่งส่วนที่เป็น LINQ แถมคุณยังมี Intellisense ให้ใช้ด้วย
เอาละครับ สำหรับภาค 1 ผมจะขอหยุดไว้ก่อน เดี๋ยวจะมีภาคต่อไปตามมาเร็วๆ นี้