2014年11月27日 星期四

[.NET] Lambda 中參考變數生命週期

Lambda,請念作浪打,就是希臘字母中的λ(波長) 不知道在那一代.NET體系後就出現了這個有趣的特性

簡單的說,Lambda-Expression是一種匿名函式的描述方式,我的習慣裡,通常配合Delegate一起服用(即時性的產生函式物件) 寫法長成這樣:

static Func delegateInstance = new Func(() => 
{
            Console.WriteLine("I was prisoned in Lambda!!!");
            return 0;
});
另外一個有趣的事情是,在CLR的記憶體模型中,
ByReference物件生命週期並不是離開函式Scope就消失,
 而是不再有人參考到此物件時,這飄渺在記憶體太空的虛無之物才會被GC回收,

所以可能會出現,在Lambda生成時,Lambda去參考函式局部範圍下的某個Object, 即便離開生成函式,一直到Lambda執行時,
這個Object仍然被保持住,而Lambda得以順利執行。
會有種 物件小姐 從 生成他的函式酒店 被"帶出場"的感覺(威~):


static int createLambda() 
{
            String theInstance = "I was created in createLambda()";
            delegateInstance = new Func(() => 
            {
                Console.WriteLine(theInstance.ToString());
                return 0;
            });

            return 0;
}
static int executeLambda()
{
            delegateInstance();
            return 0;
}
static void Main(string[] args)
{         
            createLambda();
            executeLambda();    //' theInstance still kept,  so that no NullReferenceException would raise
}

最後分享一個案例,若在Foreach中想要替陣列裡的每個物件創造對應的Lambda,在Lambda執行時,能反應陣列裡各物件的狀態(一個Lambda反應一個物件) 此時放在Lambda內的應該要用哪個具名參考?


  1. Iterator?
  2. 在Foreach外宣告一個參考? 
  3. 在Foreach內宣告參考?

       static void Main(string[] args)
        {         
          
            List theList = new List();
            Random rnd = new Random();

            Console.WriteLine("Peek answers");
            for (int i = 0; i < 10; i++)
            {
                myClass instance = new myClass();
                instance.myString = rnd.Next().ToString();
                theList.Add(instance);
                Console.WriteLine(instance.ToString());
            }
            Console.WriteLine("Peek over");


            delegateInstance = null;
            //myClass tempReference ;  //  option 2
            foreach (myClass item in theList)
            {
                //myClass tempReference = item;                       // option 3
                //tempReference = item;
                delegateInstance += new Func(() =>
                {
                    //Console.WriteLine(tempReference.ToString());
                    Console.WriteLine(item.ToString());                // option 1

                    return 0;
                });
            }

            Console.WriteLine("Lambda run");
            delegateInstance();                     // the invocation list
            Console.WriteLine("Lambda finish");

        }

答案是1、3都可(兩者效果等價),Foreach下的Reference在Iterator進到下一格時,會全部Refresh 所以各個Lambda內參考到的物件彼此是不相同的(連結到不同指標),即便程式碼中他們都是同名的變數。


 相反地,選項2用的Reference在Foreach外宣告,相當於只用一個指標去連結不同的物件 但Lambda們都是以同一個指標去參考物件,所以印出來的東西都是一樣的。

沒有留言:

張貼留言