1.Delegates
①首先Delegate無(wú)法在Objective-C找到對(duì)應(yīng)概念。
②Delegate首先是一個(gè)類,可以包含方法的地址,并且可包含多個(gè)方法。Delegate對(duì)方法進(jìn)行了一層包裝,從而可以實(shí)現(xiàn)C#的語(yǔ)言特性。C和C++中是方法地址,但并不安全,Delegate包裝一層,所以更安全。
③Lambda表達(dá)式由delegate實(shí)現(xiàn),Evnets也需要由delegate實(shí)現(xiàn)。
④Delegate比如可以用于多線程,用于泛型庫(kù)的編程,用于Event的編程。
⑤Delegate繼承自 System.MuticastDelegate,繼承自 System.Delegate
1)Delegate的聲明
public delegate void IntMethodInvoker(int x);
可以理解為delegate是一個(gè)Class類似的概念,delegate后面的方法和參數(shù)和返回值,表明了delegate保存的是什么類型的方法的地址。
2)Delegate的使用
private delegate string GetAString();
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
Console.WriteLine($"String is {firstStringMethod()}");
先定義delegate,然后new一個(gè)delegate對(duì)象,傳入對(duì)象x的方法ToString(方法作為delegate對(duì)象的構(gòu)造參數(shù)),最后調(diào)用實(shí)例化的delegate對(duì)象的執(zhí)行方法,(),可以使用invoke調(diào)用執(zhí)行。
其中
GetAString firstStringMethod = new GetAString(x.ToString);
可以縮寫為
GetAString firstStringMethod = x.ToString;
理解上仍然需要知道是new了一個(gè)delegate對(duì)象。
3) Action <T> 和 Func <T> Delegates
Action用于定義沒有返回值的Delegate,參數(shù)可以有多個(gè)
Func 用于定義有返回值的Delegate,參數(shù)可以有多個(gè)
主要目的:減少自己去定義Delegate。
比如定義一個(gè)比較大小的Delegate,用Func<T, T, bool>。
class BubbleSorter
{
static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
{
bool swapped = true;
do
{
swapped = false;
for (int i = 0; i < sortArray.Count -1; i++)
{
if (comparison(sortArray[i+1] ,sortArray[i]))
{
T temp = sortArray[i];
sortArray[i] = sortArray[i + 1];
sortArray[i + 1] = temp;
swapped = true;
}
}
} while (swapped);
}
}
在Employee中實(shí)現(xiàn)Delegate定義的比較大小的方法
public static bool CompareSalary(Employee e1, Employee e2) =>
e1.Salary < e2.Salary;
然后調(diào)用時(shí)傳入對(duì)應(yīng)的Delegate方法的定義的實(shí)現(xiàn)即可。
BubbleSorter.Sort(employees, Employee.CompareSalary);
4) 多播Delegates
一般用于保存于Delegate中的方法依次執(zhí)行,不保證有序,一般需要都是無(wú)返回值的。有返回值,只會(huì)返回最后一個(gè)執(zhí)行的方法的返回值,意義不大。注意:如果一個(gè)方法報(bào)錯(cuò),后續(xù)的方法都不會(huì)執(zhí)行了。如果仍然想要報(bào)錯(cuò)后面的方法執(zhí)行,則需要自己手寫遍歷調(diào)用。
Action<double> operations = MathOperations.MultiplyByTwo;
perations += MathOperations.Square;
perations -= MathOperations.Square;
5)匿名方法
定義:是一個(gè)代碼塊,可以傳給delegate(用delegate包裝)。用于方法只使用一次的情況。多次還是要自己去定義方法。很少用,因?yàn)橛衛(wèi)ambda表達(dá)式。
string mid = ", middle part,";
Func<string, string> anonDel = delegate(string param) {
param += mid;
param += " and this was added to the string.";
return param;
};
Console.WriteLine(anonDel("Start of string"));
思考:delegate本質(zhì)是包裝了方法后的對(duì)象。并且可以存儲(chǔ)和傳遞。在objective-c中找不到類似的概念。
2.Lambdas
1) Lambda表達(dá)式
匿名方法可以寫為L(zhǎng)AMBDA表達(dá)式,Lambda表達(dá)式本質(zhì)是一個(gè)方法包裝為了一個(gè)delegae。
string mid = ", middle part,";
Func<string, string> lambda = param => {
param += mid;
param += " and this was added to the string.";
return param;
};
Console.WriteLine(lambda("Start of string"));
2) Closure 閉包
定義:lambda表達(dá)式使用了其外部的變量。
閉包中引用的外部變量,在閉包執(zhí)行時(shí)捕獲其值。因?yàn)閳?zhí)行時(shí)才產(chǎn)生匿名類,此類copy外部的變量。類似于objective-C的block捕獲,block本質(zhì)也是一個(gè)類。原文如下:
Using the lambda expression and invoking the method creates an instance of the anonymous class and passes the value of the variable from the time when the call is made.
思考:C#的閉包還是和objective-c的閉包有很大差別,object-c的閉包可以定義__block來(lái)標(biāo)記是否可以更改block外部的對(duì)象,C#沒有這么細(xì)致的拆分。
3.Events
定義:Event通過delegate實(shí)現(xiàn),提供訂閱和發(fā)布機(jī)制。用處,比如button的點(diǎn)擊事件。Events需要傳遞,第一個(gè)參數(shù)是發(fā)送者事件者本身,第二個(gè)是其他信息。此傳遞被包裝為需繼承EventArgs的類。
定義Event
public event EventHandler<CarInfoEventArgs> NewCarInfo;
等價(jià)于定義特殊的delegate
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs
等價(jià)于
private EventHandler<CarInfoEventArgs> _newCarInfo;
public event EventHandler<CarInfoEventArgs> NewCarInfo {
add => _newCarInfo += value;
remove => _newCarInfo -= value;
}
完整的例子如下
1) 定義傳遞的消息包裝類,繼承自EventArgs
① 定義消息
public class CarInfoEventArgs:EventArgs
{
public CarInfoEventArgs(string car) => Car = car;
public string Car { get; }
}
2) 定義消息發(fā)布者,注意發(fā)布者的Invoke傳遞自己和消息。
public class CarDealer
{
② 用來(lái)保存方法
public event EventHandler<CarInfoEventArgs> NewCarInfo;
public void NewCar(string car)
{
Console.WriteLine($"CarDealer,new car{car}");
NewCarInfo?.Invoke(this,new CarInfoEventArgs(car));
}
}
3) 定義消息訂閱者,注意訂閱的方法和發(fā)布的方法的結(jié)構(gòu)一致,包括sender和消息
public class Consumer
{
private string _name;
public Consumer(string name) => _name = name;
③ 定義收到通知的處理方法
public void NewCarIsHear(Object sender, CarInfoEventArgs e)
{
Console.WriteLine($"{_name}: car{e.Car} is new");
}
}
4) 建立發(fā)布者和訂閱者的關(guān)聯(lián),注意訂閱者的執(zhí)行方法保存在發(fā)布者的delegate對(duì)象中。
public static void Main(string[] args)
{
var dealer = new CarDealer();// 發(fā)布者
var valtteri = new Consumer("Valtteri"); // 訂閱者
④ 保存方法的delegate和收到通知的方法建立聯(lián)系
dealer.NewCarInfo += valtteri.NewCarIsHear; // 建立關(guān)聯(lián)
⑤ 發(fā)送通知
dealer.NewCar("Williams"); // 發(fā)布者發(fā)布
}
思考:發(fā)布訂閱模式廣泛應(yīng)用于c#的各類應(yīng)用中。建立發(fā)布訂閱的流程項(xiàng)目objective-c較繁瑣。objective-c只需要兩個(gè)方法即可建立對(duì)象間的訂閱和發(fā)布關(guān)系,并且不用特別定義傳遞的消息,消息可以直接是自定義的類。相對(duì)來(lái)說(shuō)C#使用這種模型,就比較繁瑣,需要標(biāo)注的①②③④⑤四步,要寫的代碼也較多。這也不難理解Windowsphone為什么開發(fā)效率不高。
本文是對(duì)《Professional C# 7 and NET Core 2.0》第八章的讀書筆記。