1. 委託簡介
2. 委託組合與分解
3. 泛型委派
4.匿名方法和Lambda運算式
1.委託簡介
說來慚愧,做開發也有好幾年了,但對委託依然一知半解,項目中也很少用到,這周在家仔細的看了下,寫了點心得體會。
.net framework 基底類別庫大量地使用了委託,那麼什麼是委託呢?
大家熟悉的常用資料類型(如int)的使用方法:先定義一個變數,然後給他賦值,如
Int I;//定義變數
I=100;//給變數賦值
類似的委託也可以看成是一種資料類型,可以用於定義變數,但它所定義的變數能接收的值只能是一個方法。
舉個例子FirstDelegateExample:
//類MathOpt,定義了一下Add方法
Public cass MathOpt
{
Public int Add(int argument1,int argument2)
{
Return argument1+argument2;
}
}
//定義一個委託資料類型MathOptDelegate
Public delegate int MathOptDelegate(int value1,int value2)
上述委託跟前面定義的Add方法,兩者都接收兩個int參數,返回一個int數值,它們是不是”長得很像”,確實,這種相似不是偶然的
定義好委託資料類型後,就可以如此使用它。
MathOptDelegate oppDel; //定義此委託類型變數
MathOpt obj=new MathOpt();//執行個體化類
oppDel=obj.Add; //委託變數接收一個方法, 注意add後面沒有括弧
oppDel(1,2); //賦值之後委託變數可以當成普通方法一樣使用,等同於Add(1,2)
此項目可以得出一個直觀印像:委託可以看成是一個方法的”容器”,將某一具體的方法”裝入”後,就可以把它當成方法一樣使用。
那麼是不是所有方法都可以賦值給oppDe變數呢?
仔細看MathOptDelegate定義語句,只能接收這樣的方法,擁有兩個int參數,並且傳回值也是int。
所以只要滿足上述要求,不管其名字如何,也不管它是靜態還是執行個體,都可以賦值給oppDel,定義委託類型時對方法的要求被稱為方法的”簽名”
樣本項目DynamicInvokeMethodForCS,使用者輸入兩個運算元,再選擇運算方法,程式即可算出結果。這個樣本的關鍵之處在於它使用委託在運行時動態調用不同方法
樣本程式UseDelegateForm:點擊從表單按鈕,主表單會記錄下對按鈕的單擊次數,這個樣本關鍵在於主從窗間資訊的傳送用到了ShowInfoDelegate
2.委託組合與分解
從上面介紹的內容可知,委託變數可以引用某一方法,調用它就相當於調用這個方法
仔細想想,是不是會有以下疑惑:
如果委託僅僅是方法調用的另一種方式,那何必多此一舉引入委託這一特性?直接調用方法不是更簡潔明了?
其實委託不僅可以引用一個方法,還可以組合多個方法並批量執行它們,看以下樣本
delegate void MyDelegate(string s);
class Program
{
static void Main(string[] args)
{
MyDelegate a,b,c,d;
a=MyClass.Hello;
Console.WriteLine("調用托變數a");
a("a");
b = MyClass.GoodBye;
Console.WriteLine("調用托變數b");
b("b");
c = a + b;
Console.WriteLine("調用托變數c");
c("a+b");
d = c - a;
Console.WriteLine("調用托變數d");
d("c-a");
Console.ReadKey();
}
}
class MyClass
{
public static void Hello(string s)
{
Console.WriteLine("您好,{0}", s);
}
public static void GoodBye(string s)
{
Console.WriteLine("再見,{0}", s);
}
}
執行結果:
上述代碼委託變數c組合了委託變數a+b,因而擁有了兩個方法,當執行c(“a+b”)時將導致MyClass類的兩個靜態方法都被執行
像這樣的委託變數稱為”多路委託變數”
可以用加法運算子來給合單個委託變數為多路委託變數,同樣的用減法運算子從一個多路委託變數中除移某個委託變數
委託之所以有這種特性,是因為它內部可以包含多個方法引用,這些方法引用被稱為”委託調用列表”
3.泛型委派
首先通過一個樣本
class Program
{
static void Main(string[] args)
{
MyGenericDelegate<int> del = MyFunc;
Console.WriteLine(del(10));
Console.ReadKey();
}
static int MyFunc(int value)
{
return value;
}
}
public delegate T MyGenericDelegate<T>(T obj);
泛型委派跟普通委託類似,不同之處在於使用泛型委派時需要指定泛型參數,除此之外沒任何神秘之處。
為了方便開發,.NET基底類別庫針對在實際開發中最常用的情形提供了幾個預定義好的委託
Func系列委託用於引用一個有傳回值的方法
Func委託聲明的最後一個泛型參數是委託所接收方法的傳回值類型,前面的泛型型別參數(如果有的話)就是委託所接收方法的形參類型
樣本如下:
class Program
{
static void Main(string[] args)
{
Func<int, int, long> func = Add;
Console.WriteLine(func(10, 20));
Func<string, string, string> func1 = LinkStr;
Console.WriteLine(func1("你好:", "魚"));
Console.ReadKey();
}
static long Add(int x, int y)
{
return x + y;
}
static string LinkStr(string a, string b)
{
return a + b;
}
}
還有另外常用的預定義委託,大家自行查閱資料
Action系列委託:接收返回為void的方法
Predicate委託:引用一個返回bool值的方法
4.匿名方法和Lambda運算式
總結一下上面的使用委託步驟
1.定義委託類型
2.定義一個或多個符合委託類型要求的方法
3.定義委託類型的變數
4.將2中定義的方法引用”掛接”到3步中定義的變數,構建一個委託調用列表
5.能過委託變數間接調用方法
上述步驟是不是很煩鎖,基於上述簡化開發的考慮,C#引入了匿名方法和Lambda運算式
範例程式碼如下注意加粗字:
class Program
{
static void Main(string[] args)
{
AddDelegate del = delegate(int i, int j)
{
return i + j;
};
Console.WriteLine(del(10, 20));
Console.ReadKey();
}
}
public delegate int AddDelegate(int i,int j);
匿名方法其實是將方法定義與委託變數賦值兩個步驟合在一起,從而省掉了單獨定義一個方法的麻煩
上面的代碼如果用Lambda運算式,可以如此簡化
class Program
{
static void Main(string[] args)
{
SomeDelegateType del2 = argument => { return argument.ToString(); };
Console.WriteLine(del2(10));
Console.ReadKey();
}
}
public delegate string SomeDelegateType(int argument);
Lambda運算式其實就是匿名方法的進一步簡化,可以用於定義一個匿名函數,並將其傳送給一個委託變數
Lambda有兩種基本格式
1.(input parameter)=>運算式
2.(input parameter)=>{語句1;語句2;}
1)只有一個參數時,括弧是可選的,Lambda運算式只有一條return語句時,return關鍵字也可省
Func<int,bool> del1=(x)=>{return x>0};
可以簡化為
Func<int,bool> del1=x=>{x>0};
2)兩個或更多輸入參數由逗號分隔
Func<int,int,bool> del2=(int x,int y)=>{return x==y};
3)沒有輸入參數時,直接使用一個空的括弧()
Action del4=()=>{Console.WriteLine("No argument");};