C# 3.0: ล้ำอีกระดับ (II)

Posted 18/01/2008 01:20
by nikom
คะแนนนิยม

จากตอนแรกของบทความชุดนี้ 3 อาวุธสำคัญแรกได้ถูกบรรยายไปแล้ว ตอนที่ 2 นี้จะมาเติมเต็มคุณค่าแห่งความก้าวไกลของ C# 3.0 เข้าไปอีก ซึ่งอาวุธที่จะกล่าวถึงในตอนนี้มีความลึกซึ้งระดับเทพร้องว้าว ทีมพัฒนาของไมโครซอฟท์ ภายใต้การนำของ Anders Hejlsberg (นายแน่มาก!!) ช่างน่าปรบมือให้จริงๆ

อาวุธที่ว่าคือ Lambda Expression โดยมันเป็นสิ่งที่ทอดยาวต่อเนื่องไปยัง Expression Tree ซึ่งขอตอกย้ำกันอีกทีว่า Lambda Expression และ Expression Tree เป็นส่วนประกอบสำคัญของ LINQ (อันที่จริงต้องรวมถึงอาวุธทั้งหมดที่กล่าวถึงในบทความชุดนี้)

ขอเท้าความไปถึงความสามารถที่เรียกว่า Anonymous Method ที่ทีมพัฒนาของไมโครซอฟท์ภูมิใจนำเสนอใน C# 2.0 บางคนอาจเคยใช้งานจริงมาแล้วและอธิบายให้คนทั่วไปฟังได้ว่ามันเป็นเสมือน In-line Method ช่วยลดรูปการเรียกใช้ Delegate ซึ่งนั่นเป็นคำอธิบายที่ถูกต้องครับ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class SumDemo
{
    public List<int> Values;
    delegate T Func<T>(T a, T b);
 
    static T Plus<T>(List<T> lst, Func<T> f)
    {
        T result = default(T);
        bool first = true;
        foreach (T item in lst)
        {
            if (first)
            {
                result = item;
                first = false;
                continue;
            }
            result = f(result, item);
        }
        return result;
    }
 
    static void Main(string[] args)
    {
        SumDemo demo = new SumDemo();
        demo.Values = new List<int>(4) { 3, 4, 5, 6 };
 
        int result;
        result = Plus(
                demo.Values,
                delegate(int x, int y) { return x + y; }
            );
        Console.WriteLine("Result = {0}", result);
        Console.ReadLine();
    }
}

จากโคดข้างต้น ในบรรทัดที่ 31 เป็นตัวอย่างการใช้งาน Anonymous Method ซึ่งช่วยลดรูปการใช้ Delegate เพราะถ้าเป็น C# 1.0 หรือ 1.1 แล้วต้องสร้างเมธอดเต็มตัวขึ้นมา 1 เมธอดเพื่อบรรจุการทำงานที่เห็นในวงเล็บปีกกา ( return x + y; ) และตอนเรียกใช้ในบรรทัดที่ 31 ต้องประกาศตัวแปร Delegate พร้อมกับต้องหล่อและปลุกเสกมันด้วยชื่อเมธอดที่สร้างขึ้น แล้วจึงเรียกทำงานได้ เฮ้อ..วุ่นวายใช้ได้เลย

Anonymous Method นั้นว่าช่วยลดรูปแล้ว พอมาถึงเวอร์ชัน 3 ด้วยความสามารถของ Lambda Expression แล้วยิ่งไปกันใหญ่เลย แทบจะต้องเพ่งดูกันเลยครั้งแรกๆที่พยายามจะมอง เพราะมันแทบไม่เหลืออะไรให้รู้ว่าว่าเป็น Anonymous Method ดีนะที่ยังมีโอเปอเรเตอร์ '=>' ให้สังเกต มาดูกันใหม่ว่าถ้าใช้ Lambda Expression แล้ว บรรทัดที่ 29 ถึง 31 ในโคดตัวอย่างข้างบนจะเป็นอย่างนี้

1
2
3
4
result = Plus(
        demo.Values,
        (int x, int y) => { return x + y; }
    );

ถ้านั่นไม่หนำใจ แบบนี้ก็ยังได้

1
2
3
4
result = Plus(
        demo.Values,
        (x, y) => { return x + y; }
    );

แบบหลังนี้กลไก C# จะรู้ไทป์ของ x กับ y เพราะ Plus เป็นเจเนอริคเมธอด ลองย้อนกลับไปดูโคดข้างบนสุดอีกทีสิครับ เห็นแล้วใช่มั้ยว่าไทป์ของ demo.Values เป็นต้นแบบให้ส่วนที่เหลือของเมธอด

ดูทางด้านขวาของโอเปอเรเตอร์ '=>' ในตัวอย่างหลังสุดกันนิด เครื่องหมาย {} ถูกใช้ครอบคำสั่ง return ซึ่งถ้ามีแค่คำสั่งเดียวแบบนี้ โละมันทิ้งไปก็ได้ (แทบไม่เหลือแล้ว)

1
2
3
4
result = Plus(
        demo.Values,
        (x, y) => x + y
    );

ยังครับยัง ยังไม่งามที่สุด ถ้าลองออกแบบ Delegate และวิธีการคำนวณ result กันใหม่ โดยใช้โอเปอเรเตอร์ '+=' เข้ามาร่วมทำงานสำหรับการบวกค่าในลิสต์ทบกันไปเรื่อยๆ จะได้เป็น

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class SumDemo
{
    public List<int> Values;
    delegate T Func<T>(T x);
 
    static T Plus<T>(List<T> lst, Func<T> f)
    {
        T result = default(T);
        bool first = true;
 
        foreach (T item in lst)
        {
            result = f(item);
        }
        return result;
    }
 
    static void Main(string[] args)
    {
        SumDemo demo = new SumDemo();
        demo.Values = new List<int> { 3, 4, 5, 6 };
        int result = 0;
        result = Plus(
                demo.Values,
                (x) => result += x
            );
        Console.WriteLine("Result = {0}", result);
        Console.ReadLine();
    }
}

เป็นโคดที่น่าทึ่งจริงๆ แต่ที่สวยเพรียวที่สุดต้องแบบนี้

1
2
3
4
result = Plus(
        demo.Values,
        x => result += x
    );

จากก้อนโคดแรกสุดถึงล่าสุดของบทความตอนนี้ ทำให้ได้เห็นถึงบทบาทและสารพัดหน้าตาของ Lambda Expression ซึ่งเป็นการคำนวณธรรมดาพื้นๆ ย้อนกลับไปที่ Anonymous Method ถ้าบางคนเคยใช้จริงแล้ว ขอเดาไว้เลยว่าส่วนใหญ่แล้วใช้เป็น Predicate (เงื่อนไข) เช่น การค้นหาไอเท็มในลิสต์ด้วยเงื่อนไขบางอย่างตามต้องการ และเงื่อนไขที่ว่าก็จะเขียนเป็น Anonymous Method  ...และถ้ากำลังเดาอยู่ในใจว่า Lambda Expression สามารถนำมาใช้เพื่อจุดประสงค์เดียวกันนี้ได้ใช่หรือไม่ ...แน่นอนเลยใช้ได้แน่ และอันที่จริงต้องกล่าวว่าเป็นงานหลักของมันด้วยซ้ำไป ดูตัวอย่างโคดนี้เพื่อสนับสนุนความคิด

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main(string[] args)
{
    string[] countries = { "Thailand", "England", "USA" };
    ShowResult(countries, c => c.Contains("land"));
}
  
public static void ShowResult<T>(T[] countries, Func<T, bool> screen)
{
    foreach (T c in countries)
    {
        if (screen(c))
            Console.WriteLine(c);
    }
}

และด้วยความที่นำมาใช้เป็น Predicate นี่เอง จึงต้องว่ากันต่อไปยังการทำงานที่ต้องมีการตัดสินใจหรือการคัดเลือกด้วยหลายเงื่อนไข หลายคนน่าจะเคยเขียนสเตทเมนท์ SQL โดยที่คลอส WHERE มีเงื่อนไขจำนวนมาก เงื่อนไขเหล่านั้นอาจจะเชื่อมกันแบบ AND หรือ OR เป็นคู่ๆทบกันไปเรื่อยๆ จินตนาการนี้ใช้ได้กับ Expression Tree ร้อยเปอร์เซนต์

Expression Tree เป็นการรวบรวม Predicates (อาจจะมากกว่าหนึ่งก็ได้) ในรูป Lambda Expression เอาไว้ในตัวมัน การทำงานจริงกับ .NET Framework 3.5 จะอาศัยกลุ่มคลาสที่อยู่ในตระกูล Expression ที่อยู่ในเนมสเปส System.Linq.Expressions โดยเฉพาะอย่างยิ่งเจเนอริคคล

Published Jan 18 2008, 01:20 AM by nikom
Filed under: ,
 

นิ้วโป้ง said:

ขอบคุณมากครับ ดีมากเลยครับ

August 21, 2008 1:33 PM
(required)  
(optional)
(required)  
Add
คอแหลม
โฆษณาออนไลน์,
				โฆษณา,ออนไลน์,ลงโฆษณา,ประกาศ,online advertising,online
				,advertising,โปรโมทสินค้า,โปรโมทเว็บไซต์,promote website,
				seo,pay per click,ad per click,media,ค้นหาเว็บ,media,
				สื่อ