1. 程式人生 > >C#Lambda表示式

C#Lambda表示式

需求

有時候我們需要傳遞一個很方法的引用,我們很確定這個方法僅僅會呼叫這一次,單獨為它建立一個方法感覺有些浪費,但是又必須用到這個方法。又或者臨時需要一個方法,但是思考半天想不出該給這個方法取什麼名字(有過這個經歷的同學握個爪)。這個時候Lambda就派上用場了。

是什麼

Lambda 表示式是一種可用於建立委託或表示式目錄樹型別的匿名函式。通過使用 lambda 表示式,可以寫入可作為引數傳遞或作為函式呼叫值返回的本地函式。Lambda 表示式對於編寫 LINQ 查詢表示式特別有用。
若要建立 Lambda 表示式,需要在 Lambda 運算子 => 左側指定輸入引數(如果有),然後在另一側輸入表示式或語句塊。例如,lambda 表示式 x => x * x 指定名為 x 的引數並返回 x 的平方值,你可以將此表示式分配給委託型別。

說明

  • => 運算子具有與賦值運算子 (=) 相同的優先順序並且是右結合運算(參見“運算子”文章的“結合性”部分)。
  • Lambda 在基於方法的 LINQ 查詢中用作標準查詢運算子方法(如 Where)的引數。
  • 使用基於方法的語法在 Enumerable 類中呼叫 Where 方法時(如在 LINQ to Objects 和
    LINQ to XML 中一樣),引數是委託型別System.Func<T, TResult>。使用 Lambda表示式建立該委託最為方便。例如,當你在 System.Linq.Queryable 類中呼叫相同的方法時(如在LINQ to SQL中一樣),引數型別為 System.Linq.Expressions.Expression,其中 Func是最多具有十六個輸入引數的任何一個Func委託。同樣,Lambda 表示式只是一種非常簡潔的構造該表示式目錄樹的方式。儘管事實上通過Lambda建立的物件具有不同的型別,但Lambda 使得 Where呼叫看起來類似。

注意

  •  如果Lambda表示式要獲取引數,就在=>操作符左側的圓括號內指定。可省略引數型別,C#編譯器能根據Lambda表示式的上下文進行推斷。如果希望Lambda表示式永久(而不是區域性)更改引數值,可以用“傳引用”的方式傳遞引數(使用ref關鍵字),但不推薦這樣做。
  •  Lambda表示式可以返回值,但返回型別必須與即將新增這個Lambda表示式的委託的型別匹配。
  •  Lambda表示式的主體可以是簡單表示式,也可以是C#程式碼塊(程式碼塊可包含多個語句、方法呼叫、變數定義等等)。
  •  Lambda表示式方法中定義的變數會在方法結束時離開作用域。
  •  Lambda表示式可訪問和修改Lambda表示式外部的所有變數,只要那些變數在Lambda表示式定義時,和Lambda表示式處在相同的作用域中。

怎麼用

準備工作

委託定義

public delegate void LambdaHandle0();
public delegate void LambdaHandle0s();
public delegate void LambdaHandle1(int x);
public delegate void LambdaHandle2(int x,int y);
public delegate void LambdaHandle3(ref int x,int y); 

事件定義

public class LambdaEvent{
    //無參事件1
    public event LambdaHandle0 thisEvent0;
    //無參事件2
    public event LambdaHandle0s thisEvent0s;
    //一個整形引數事件
    public event LambdaHandle1 thisEvent1;
    //兩個整形引數事件
    public event LambdaHandle2 thisEvent2;
    //兩個整形引數事件,第一個引數為引用傳遞
    public event LambdaHandle3 thisEvent3;

    public void callEvent0(){
        if(thisEvent0!=null)
            thisEvent0 ();
    }

    public void callEvent0s(){
        if(thisEvent0s!=null)
            thisEvent0s ();
    }

    public void callEvent1(int x){
        if(thisEvent1!=null)
            thisEvent1 (x);
    }

    public void callEvent2(int x,int y){
        if(thisEvent2!=null)
            thisEvent2 (x,y);
    }
    public void callEvent3(ref int x,int y){
        if(thisEvent3!=null)
            thisEvent3 (ref x,y);
    }
} 

用於事件的方法定義

private void myFunction(string str){
        Debug.Log ("str="+str);
} 

具體用法

例項化事件

LambdaEvent myEvent=new LambdaEvent();

###無參表示式

myStr="myStr";
myEvent.thisEvent0+=()=>myFunction(myStr);
myEvent.callEvent0 ();

這裡寫圖片描述

myEvent.thisEvent0s+=()=>Debug.Log("Lambda");
myEvent.callEvent0s (); 

這裡寫圖片描述

一個引數的表示式

int a=10;
//引數型別省略,自動根據上下文判斷
myEvent.thisEvent1 +=x => Debug.Log("x*x="+ x * x);
myEvent.callEvent1 (a); 

這裡寫圖片描述

多個引數的表示式

int a=10,b=10;
myEvent.thisEvent2+=(x,y)=>{x=x*y;Debug.Log("x="+x);};
myEvent.callEvent2 (a,b);
//普通傳值,引數值不變
Debug.Log ("a="+a); 

這裡寫圖片描述

傳遞引用引數的表示式

//第一個引數為引用。注意此時必須指明引數型別
myEvent.thisEvent3+=(ref int x,int y)=>{x=x*y;Debug.Log("x="+x);};
myEvent.callEvent3 (ref a,b);
Debug.Log ("ref a="+a); 

這裡寫圖片描述

帶有標準查詢運算子的Lambda

//用於泛型委託Func
Func<int, bool> myFunc = x => x == 5;
Debug.Log("myFunc(5)="+myFunc(5));

這裡寫圖片描述

//用於Linq查詢
int[] number = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
Debug.Log ("Count="+number.Count(n=>n%2==1));

這裡寫圖片描述

List<int> numList = number.TakeWhile (n => n < 6).ToList ();
if (numList != null)
    foreach (int i in numList)
        Debug.Log (i); 

這裡寫圖片描述

完整指令碼

using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

public delegate void LambdaHandle0();
public delegate void LambdaHandle0s();
public delegate void LambdaHandle1(int x);
public delegate void LambdaHandle2(int x,int y);
public delegate void LambdaHandle3(ref int x,int y);

public class MyLambda : MonoBehaviour {
    void Start () {
        LambdaEvent myEvent=new LambdaEvent();
        string myStr="myStr";
        myEvent.thisEvent0+=()=>myFunction(myStr);
        myEvent.callEvent0 ();

        myEvent.thisEvent0s+=()=>Debug.Log("Lambda");
        myEvent.callEvent0s ();

        int a=10,b=10;
        //引數型別省略,自動根據上下文判斷
        myEvent.thisEvent1 +=x => Debug.Log("x*x="+ x * x);
        myEvent.callEvent1 (a);

        myEvent.thisEvent2+=(x,y)=>{x=x*y;Debug.Log("x="+x);};
        myEvent.callEvent2 (a,b);
        //普通傳值,引數值不變
        Debug.Log ("a="+a);

        //第一個引數為引用。注意此時必須指明引數型別
        myEvent.thisEvent3+=(ref int x,int y)=>{x=x*y;Debug.Log("x="+x);};
        myEvent.callEvent3 (ref a,b);
        Debug.Log ("ref a="+a);

        //用於泛型委託Func
        Func<int, bool> myFunc = x => x == 5;
        Debug.Log("myFunc(5)="+myFunc(5));

        //用於Linq查詢
        int[] number = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
        Debug.Log ("Count="+number.Count(n=>n%2==1));

        List<int> numList = number.TakeWhile (n => n < 6).ToList ();
        if (numList != null)
            foreach (int i in numList)
                Debug.Log (i);


    }
    private void myFunction(string str){
        Debug.Log ("str="+str);
    }
}

public class LambdaEvent{
    //無參事件1
    public event LambdaHandle0 thisEvent0;
    //無參事件2
    public event LambdaHandle0s thisEvent0s;
    //一個整形引數事件
    public event LambdaHandle1 thisEvent1;
    //兩個整形引數事件
    public event LambdaHandle2 thisEvent2;
    //兩個整形引數事件,第一個引數為引用傳遞
    public event LambdaHandle3 thisEvent3;

    public void callEvent0(){
        if(thisEvent0!=null)
            thisEvent0 ();
    }

    public void callEvent0s(){
        if(thisEvent0s!=null)
            thisEvent0s ();
    }

    public void callEvent1(int x){
        if(thisEvent1!=null)
            thisEvent1 (x);
    }

    public void callEvent2(int x,int y){
        if(thisEvent2!=null)
            thisEvent2 (x,y);
    }
    public void callEvent3(ref int x,int y){
        if(thisEvent3!=null)
            thisEvent3 (ref x,y);
    }
}