-
#1. 委托
- 1.1 聲明委托
- 1.2 使用委托
- 1.3 簡(jiǎn)單委托示例
- 1.4 Action<T>和Func<T>委托
- 1.5 BubbleSorter示例
- 1.6 多播委托
- 1.7 匿名方法
-
#2. Lambda 表達(dá)式
- 2.1 參數(shù)
- 2.2 多行代碼
- 2.3 Lambda表達(dá)式外部變量
-
#3. 事件
- 3.1 事件發(fā)布程序
- 3.2 事件偵聽(tīng)器
- 3.3 弱事件
#1. 委托
當(dāng)要把方法傳遞給其他方法時(shí),需要使用委托。把方法的細(xì)節(jié)封裝在一種新類型的對(duì)象中,即委托。委托只是一種特殊類型的對(duì)象,其特殊之處在于,我們以前定義的所有對(duì)象都包含數(shù)據(jù),而委托包含的只是一個(gè)或多個(gè)方法的地址。
1.1 聲明委托
在C#中使用一個(gè)類時(shí),分兩個(gè)階段。首先,需要定義這個(gè)類,即告訴編譯器這個(gè)類由什么字段和方法組成。然后(除非只使用靜態(tài)方法),實(shí)例化類的一個(gè)對(duì)象。使用委托時(shí),也需要經(jīng)過(guò)這兩個(gè)步驟。
- 首先必須定義要使用的委托,對(duì)于委托,定義它就是告訴編譯器這種類型的委托表示哪種類型的方法。
- 然后,必須創(chuàng)建該委托的一個(gè)或多個(gè)實(shí)例。編譯器在后臺(tái)將創(chuàng)建表示該委托的一個(gè)類。
定義委托的語(yǔ)法如下:
delegate void IntMethodInvoker(int x);
在定義委托時(shí),必須給出它所表示的方法的簽名和返回類型等全部細(xì)節(jié)。
==理解委托的一種好方式是把委托當(dāng)作這樣一件事情,它給方法的簽名和返回類型指定名稱。==
1.2 使用委托
下面的代碼說(shuō)明了如何使用委托。
private delegate string GetAString();
static void Main(string[] args)
{
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
Console.WriteLine("String is {0}", firstStringMethod());
//With firstStringMethod initialized to x.ToString(),
//the above statement is equivalent to saying
//Console.WriteLine("String is {0}",x.ToString());
}
在這段代碼中,實(shí)例化了類型GetAString的一個(gè)委托,并對(duì)它進(jìn)行初始化,使它引用整形變量x的ToString()。在C#中,委托在語(yǔ)法上總是接受一個(gè)參數(shù)的構(gòu)造函數(shù),這個(gè)參數(shù)就是委托引用的方法。這個(gè)方法必須匹配最初定義委托時(shí)的簽名。所以在這個(gè)示例中,如果不帶參數(shù)并返回一個(gè)字符串的方法來(lái)初始化firstStringMethod變量,就會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤。注意,因?yàn)閕nt.ToString()是一個(gè)實(shí)例方法(不是靜態(tài)方法),所以需要指定實(shí)例(x)和方法名來(lái)正確地初始化委托。
實(shí)際上,給委托實(shí)例提供圓括號(hào)與調(diào)用委托類的Invoke()方法完全相同。因?yàn)閒irstStringMethod是委托類型的一個(gè)變量,所以c#編譯器會(huì)用firstStringMethod.Invoke()代替firstStringMethod()。
firstStringMethod();
firstStringMethod.Invoke();
為了減少輸入量,只要需要委托實(shí)例,就可以只傳送地址的名稱。這稱為委托推斷。只要編譯器可以把委托實(shí)例解析為特定的類型,這個(gè)C#特性就是有效的。下面用GetAString委托的一個(gè)新實(shí)例初始化GetAString類型的firstStringMethod變量:
GetAString firstStringMethod = new GetAString(x.ToString);
只要用變量x把方法名傳送給變量firstStringMethod,就可以編寫出作用相同的代碼:
GetAString firstStringMethod = x.ToString;
==調(diào)用上述方法時(shí)輸入形式不能為x.ToString()(不要輸入圓括號(hào)),也不能把它傳送給委托變量,輸入圓括號(hào)調(diào)用一個(gè)方法,調(diào)用x.ToString()方法會(huì)返回一個(gè)不能賦予委托變量的字符串對(duì)象,只能把方法的地址賦予委托變量。==
委托推斷可以在需要委托實(shí)例的任何地方使用。委托推斷也可以用于事件,因?yàn)槭录谖小?/p>
==給委托的實(shí)例可以引用任何類型的任何對(duì)象上的實(shí)例方法或靜態(tài)方法——只要方法的簽名匹配于類型的簽名即可。==
1.3 簡(jiǎn)單的委托示例
在這個(gè)示例中,定義一個(gè)類MathsOperations,它有兩個(gè)靜態(tài)方法,對(duì)double類型的值執(zhí)行兩個(gè)操作,然后使用該委托調(diào)用這些方法。
class MathOperations
{
public static double MultiplyByTwo(double value)
{
return value * 2;
}
public static double Square(double value)
{
return value * value;
}
}
下面調(diào)用方法:
class DelegateTest
{
private delegate double DoubleOp(double x);
static void Main(string[] args)
{
DoubleOp[] operations = {
MathOperations.MultiplyByTwo,
MathOperations.Square
};
for (int i = 0; i < operations.Length; i++)
{
Console.WriteLine("Using operations[{0}]:", i);
ProcessAndDisplayName(operations[i], 2.0);
ProcessAndDisplayName(operations[i], 7.94);
ProcessAndDisplayName(operations[i], 1.414);
Console.WriteLine();
}
}
static void ProcessAndDisplayName(DoubleOp action, double value)
{
double result = action(value);
Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}
}
在這段代碼中,實(shí)例化了一個(gè)委托數(shù)組DoubleOp(記住,一旦定義了委托類,基本上就可以實(shí)例化它的實(shí)例,就像處理一般的類那樣——所以把一些委托的實(shí)例放在數(shù)組中是可以的)。該數(shù)組的每個(gè)元素都初始化為由MathsOperations類實(shí)現(xiàn)的不同操作。
1.4 Action<T>和Func<T>委托
除了為每個(gè)參數(shù)和返回類型定義一個(gè)新委托類型之外,還可以使用Action<T>和Func<T>委托。泛型Action<T>委托表示引用一個(gè)void返回類型的方法。
Func<double, double>[] operations = {
MathOperations.MultiplyByTwo,
MathOperations.Square
};
static void ProcessAndDisplayName(Func<double,double> action, double value)
{
double result = action(value);
Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}
1.5 BubbleSorter示例
下面的示例將說(shuō)明委托的真正用途。我們要編寫一個(gè)類BubbleSorter,它實(shí)現(xiàn)一個(gè)靜態(tài)方法Sort(),這個(gè)方法的第一個(gè)參數(shù)是一個(gè)對(duì)象數(shù)組,把該數(shù)組按照升序重新排列。
對(duì)于接受類型T的泛型方法Sort<T>,需要一個(gè)比較方法,其兩個(gè)參數(shù)的類型是T,if比較的返回類型是布爾類型。這個(gè)方法可以從Func<T1,T2,TResult>委托中引用,其中T1和T2的類型相同:Func<T,bool>。給Sort<T>方法指定下述簽名:
static public void Sort<T>(IList<T> sortArray,Func<T,T,bool> comparison);
這個(gè)方法的文檔說(shuō)明,comparison必須引用一個(gè)方法,該方法帶有兩個(gè)參數(shù),如果第一個(gè)參數(shù)的值“小于”第二個(gè)參數(shù),就返回true。
設(shè)置完畢后,下面定義BubbleSorter類:
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 tmp = sortArray[i];
sortArray[i] = sortArray[i + 1];
sortArray[i + 1] = tmp;
swapped = true;
}
}
} while (swapped);
}
}
為了使用這個(gè)類,需要定義另一個(gè)類,從而建立要排序的數(shù)組。
class Employee
{
public string Name { get; private set; }
public decimal Salary { get; private set; }
public Employee(string name, decimal salary)
{
this.Name = name;
this.Salary = salary;
}
public override string ToString()
{
return string.Format("{0}, {1:C}", Name, Salary);
}
public static bool CompareSalary(Employee l, Employee r)
{
return l.Salary < r.Salary;
}
}
注意,為了匹配Func<T,T,bool>委托的簽名,在這個(gè)類中必須定義CompareSalary,它的參數(shù)是兩個(gè)Employee引用,并返回一個(gè)布爾值,在實(shí)現(xiàn)比較代碼中,根據(jù)薪水進(jìn)行比較。
下面編寫一些客戶端代碼,完成排序:
class DelegateTest
{
static void Main(string[] args)
{
Employee[] employees = {
new Employee("Bugs Bunny",20000),
new Employee("Elmer Fudd",10000),
new Employee("Daffy Duck",25000),
new Employee("Will Coyote",1000000.38m),
new Employee("Foghorn Leghorn",23000),
new Employee("RoadRunner",50000),
};
BubbleSorter.Sort(employees, Employee.CompareSalary);
foreach(var employee in employees)
{
Console.WriteLine(employee);
}
}
}
1.6 多播委托
前面使用的每個(gè)委托都只包含一個(gè)方法調(diào)用。調(diào)用委托的次數(shù)與調(diào)用方法的次數(shù)相同。如果要調(diào)用多個(gè)方法,就需要多次顯式調(diào)用這個(gè)委托。但是,委托也可以包含多個(gè)方法。這種委托稱為多播委托。如果調(diào)用多播委托,就可以按順序連續(xù)調(diào)用多個(gè)方法。為此,委托的簽名就必須返回void;否則,就只能得到委托調(diào)用的最后一個(gè)方法的結(jié)果。
可以使用返回類型為void的Action<double>委托:
class DelegateTest
{
static void Main(string[] args)
{
Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;
}
}
在前面的示例中,因?yàn)橐鎯?chǔ)對(duì)兩個(gè)方法的引用,所以實(shí)例化了一個(gè)委托數(shù)組。而這里只是在同一個(gè)多播委托中添加兩個(gè)操作。多播委托可以識(shí)別運(yùn)算符"+"和"+="。
要委托引用返回void的方法,就應(yīng)重寫MathOperations類中的方法,從而讓它們顯示其結(jié)果,而不是返回它們:
class MathOperations
{
public static void MultiplyByTwo(double value)
{
double result = value * 2;
Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result);
}
public static void Square(double value)
{
double result = value * value;
Console.WriteLine("Squaring: {0} gives {1}", value, result);
}
}
為了適應(yīng)這個(gè)改變,也必須重寫ProcessAndDisplayName()方法:
static void ProcessAndDisplayName(Action<double> action, double value)
{
Console.WriteLine();
Console.WriteLine("ProcessAndDisplayName called with value = {0}", value);
action(value);
}
下面測(cè)試多播委托,其代碼如下:
class DelegateTest
{
static void Main(string[] args)
{
Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;
ProcessAndDisplayName(operations, 2.0);
ProcessAndDisplayName(operations, 7.94);
ProcessAndDisplayName(operations, 1.414);
Console.WriteLine();
}
}
如果正在使用多播委托,就應(yīng)知道對(duì)同一個(gè)委托調(diào)用方法鏈的順序并未正式定義。因此應(yīng)避免編寫依賴于以特定順序調(diào)用方法的代碼。
通過(guò)一個(gè)委托調(diào)用多個(gè)方法還可能導(dǎo)致一個(gè)大問(wèn)題。多播委托包含一個(gè)逐個(gè)調(diào)用的委托集合。如果通過(guò)委托調(diào)用的其中一個(gè)方法拋出異常,整個(gè)迭代就會(huì)停止。
class DelegateTest
{
static void Main(string[] args)
{
Action d1 = One;
d1 += Two;
try
{
d1();
}
catch (Exception e)
{
Console.WriteLine("Exception caught with msg:{0}",e.Message);
}
}
static void One()
{
Console.WriteLine("One");
throw new Exception("Error in one");
}
static void Two()
{
Console.WriteLine("Two");
}
}
委托只調(diào)用了第一個(gè)方法。因?yàn)榈谝粋€(gè)方法拋出了異常,所以委托的迭代會(huì)停止,不再調(diào)用Two()。
在這種情況下,為了避免這個(gè)問(wèn)題,應(yīng)自己迭代方法列表。Delegate類定義GetInvocationList(),它返回一個(gè)Delegate數(shù)組。現(xiàn)在可以使用這個(gè)委托調(diào)用與委托直接相關(guān)的方法,捕獲異常,并繼續(xù)下一次迭代。
class DelegateTest
{
static void Main(string[] args)
{
Action d1 = One;
d1 += Two;
Delegate[] delegates = d1.GetInvocationList();
foreach(Action d in delegates)
{
try
{
d();
}
catch (Exception e)
{
Console.WriteLine("Exception caught with msg:{0}", e.Message);
}
}
}
}
1.7 匿名方法
到目前為止,要想使委托工作,方法必須已經(jīng)存在(即委托是用它將調(diào)用的方法的相同的簽名定義的)。但還有另外一種使用委托的方式:即通過(guò)匿名方法。匿名方法是用作委托的參數(shù)的一段代碼。
用匿名方法定義委托的語(yǔ)法與前面的定義并沒(méi)有區(qū)別。但在實(shí)例化委托時(shí),就有區(qū)別了。
class DelegateTest
{
static void Main(string[] args)
{
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"));
}
}
Func<string,string>委托接受一個(gè)字符串參數(shù),返回一個(gè)字符串。anonDel是這種委托類型的變量。不是把方法名賦予這個(gè)變量,而是使用一段簡(jiǎn)單的代碼:它前面是關(guān)鍵字delegate,后面是一個(gè)字符串參數(shù)。
在使用匿名方法時(shí),必須遵循兩條規(guī)則。在匿名方法中不能使用跳轉(zhuǎn)語(yǔ)句(break、goto或continue)跳到該匿名方法的外部,反之亦然:匿名方法外部的跳轉(zhuǎn)語(yǔ)句不能跳到該匿名方法的內(nèi)部。
在匿名方法內(nèi)部不能訪問(wèn)不安全的代碼。另外,也不能訪問(wèn)在匿名方法外部使用的ref或out參數(shù)。但可以使用在匿名外部定義的其他變量。
#2. Lambda表達(dá)式
自C#3.0開(kāi)始,就可以使用一種新語(yǔ)法把實(shí)現(xiàn)代碼賦予委托:Lambda表達(dá)式。只要有委托參數(shù)類型的地方,就可以使用Lambda表達(dá)式。前面使用匿名方法的例子可以改為使用Lambda表達(dá)式:
class LambdaTest
{
static void Main(string[] args)
{
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"));
}
}
Lambda運(yùn)算符"=>"的左邊列出了需要的參數(shù)。Lambda運(yùn)算符的右邊定義了賦予lambda變量的方法的實(shí)現(xiàn)代碼。
2.1 參數(shù)
Lambda表達(dá)式有幾種定義參數(shù)的方式。如果只有一個(gè)參數(shù),只寫出參數(shù)名字就足夠了。下面的Lambda表達(dá)式使用了參數(shù)s。因?yàn)槲蓄愋投x了一個(gè)string參數(shù),所以s的類型就是string。
Func<string,string> oneParam = s => String.Format(
"change uppercase {0}",s.ToUpper());
Console.WriteLine(oneParam("test"));
如果委托使用多個(gè)參數(shù),就把參數(shù)名放在花括號(hào)中。這里參數(shù)x和y的類型是double,由Func<double,double,double>委托定義:
Func<double,double,double> twoParams = (x,y) => x * y;
Console.WriteLine(twoParams(3,2));
為了方便,可以在花括號(hào)中給變量名添加參數(shù)類型:
Func<double, double, double> twoParamsWithTypes = (double x, double y) => x * y;
Console.WriteLine(twoParamsWithTypes(4, 2));
2.2 多行代碼
如果Lambda表達(dá)式只有一條語(yǔ)句,在方法塊內(nèi)就不需要花括號(hào)和return語(yǔ)句,因?yàn)榫幾g器會(huì)添加一條隱式的return語(yǔ)句。
Func<double,double> square = x => x * x;
添加花括號(hào)、return語(yǔ)句和分號(hào)是完全合法的,通常這比不添加這些符號(hào)更容易閱讀:
Func<double,double> square = x => {
return x * x;
}
但是,如果在Lambda表達(dá)式的實(shí)現(xiàn)代碼中需要多條語(yǔ)句,就必須添加花括號(hào)和return語(yǔ)句:
Func<string,string> lambda = param => {
param += mid;
param += " and this was added to the string";
return param;
}
2.3 Lambda表達(dá)式外部的變量
通過(guò)Lambda表達(dá)式可以訪問(wèn)Lambda表達(dá)式塊外部的變量。這是一個(gè)非常好的功能,但如果為正確使用,也會(huì)非常危險(xiǎn)。
在下面的示例中,F(xiàn)unc<int,int>類型的Lambda表達(dá)式需要一個(gè)int參數(shù),返回一個(gè)int。該Lambda表達(dá)式的參數(shù)用變量x定義。實(shí)現(xiàn)代碼還訪問(wèn)了Lambda表達(dá)式外部的變量someVal。只要不認(rèn)為在調(diào)用f時(shí),Lambda表達(dá)式創(chuàng)建了一個(gè)以后使用的新方法,這似乎沒(méi)有什么問(wèn)題。
int someVal = 5;
Func<int, int> f = x => x + someVal;
Console.WriteLine(f(10));
對(duì)于Lambda表達(dá)式x => x + someVal,編譯器會(huì)創(chuàng)建一個(gè)匿名類,它有一個(gè)構(gòu)造函數(shù)來(lái)傳遞外部變量。該構(gòu)造函數(shù)取決于從外部傳遞進(jìn)來(lái)的變量個(gè)數(shù)。
public class AnonymousClass
{
private int someVal;
public AnonymousClass(int someVal)
{
this.someVal = someVal;
}
public int AnonymouseMethod(int x) {
return x + someVal;
}
}
使用Lambda表達(dá)式并調(diào)用該方法,會(huì)創(chuàng)建匿名類的一個(gè)實(shí)例,并傳遞調(diào)用該方法時(shí)變量的值。
==Lambda表達(dá)式可以用于類型是一個(gè)委托的任意地方。類型是Expression或Expression<T>時(shí),也可以使用Lambda表達(dá)式。此時(shí)編譯器會(huì)創(chuàng)建一個(gè)表達(dá)式樹(shù)。==
#3. 事件
事件基于委托,為委托提供了一種發(fā)布/訂閱機(jī)制。
3.1 事件發(fā)布程序
從CarDealer類開(kāi)始,它基于事件提供一個(gè)訂閱。CarDealer類用event關(guān)鍵字定義了類型為EventHandler<CarInfoEventArgs>的NewCarInfo事件。在NewCar()方法中,觸發(fā)NewCarInfo事件:
public class CarIInfoEventArgs : EventArgs
{
public string Car { get; private set; }
public CarIInfoEventArgs(string car)
{
this.Car = car;
}
}
public class CarDealer
{
public event EventHandler<CarIInfoEventArgs> NewCarInfo;
public void NewCar(string car)
{
Console.WriteLine("CarDealer, new car {0}", car);
NewCarInfo?.Invoke(this, new CarIInfoEventArgs(car));
}
}
CarDealer類提供了EventHandler<CarIInfoEventArgs>類型的NewCarInfo事件。作為一個(gè)約定,事件一般使用帶兩個(gè)參數(shù)的方法,其中第一個(gè)參數(shù)是一個(gè)對(duì)象,包含事件的發(fā)送者,第二個(gè)參數(shù)提供了事件的相關(guān)信息。
委托EventHandler<TEventArgs>的定義如下:
public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e)
where TEventArgs : EventArgs
3.2 事件偵聽(tīng)器
Consumer類用作事件偵聽(tīng)器。這個(gè)類訂閱了CarDealer類的事件,并定義了NewCarIsHere方法,該方法滿足EventHandler<CarInfoEventArgs>委托的要求,其參數(shù)類型是object和CarInfoEventArgs:
public class Consumer
{
private string name;
public Consumer(string name)
{
this.name = name;
}
public void NewCarIsHere(object sender, CarIInfoEventArgs e)
{
Console.WriteLine("{0}: car {1} is new", name, e.Car);
}
}
現(xiàn)在需要連接事件發(fā)布程序和訂閱器。為此使用CarDealer類的NewCarInfo事件,通過(guò)“+=”創(chuàng)建一個(gè)訂閱。消費(fèi)者訂閱了事件,接著消費(fèi)者也訂閱了事件,然后通過(guò)“-=”取消了訂閱。
static void Main(string[] args)
{
ar dealer = new CarDealer();
var michael = new Consumer("Michael");
dealer.NewCarInfo += michael.NewCarIsHere;
dealer.NewCar("Mercedes");
var nick = new Consumer("Nick");
dealer.NewCarInfo += nick.NewCarIsHere;
dealer.NewCar("Ferrari");
dealer.NewCarInfo -= michael.NewCarIsHere;
dealer.NewCar("Toyota");
}
3.3 弱事件
通過(guò)事件,直接連接到發(fā)布程序和偵聽(tīng)器。但垃圾回收有一個(gè)問(wèn)題。例如,如果偵聽(tīng)器不再直接飲用,發(fā)布程序就仍有一個(gè)引用。垃圾回收器不能清空偵聽(tīng)器占用的內(nèi)存,因?yàn)榘l(fā)布程序仍保有一個(gè)引用,會(huì)針對(duì)偵聽(tīng)器觸發(fā)事件。
這種強(qiáng)連接可以通過(guò)弱事件模式來(lái)解決,即使用WeekEventManager作為發(fā)布程序和偵聽(tīng)器之間的中介。
1. 弱事件管理器
要使用弱事件,需要?jiǎng)?chuàng)建一個(gè)派生自WeekEventManager類的類。WeekEventManager類
public class WeakCarInfoEventManager : WeakEventManager
{
public static WeakCarInfoEventManager CurrentManager
{
get
{
WeakCarInfoEventManager manager = GetCurrentManager(typeof(WeakCarInfoEventManager))
as WeakCarInfoEventManager;
if (manager == null)
{
manager = new WeakCarInfoEventManager();
SetCurrentManager(typeof(WeakCarInfoEventManager), manager);
}
return manager;
}
}
public static void AddListener(object source, IWeakEventListener listener)
{
CurrentManager.ProtectedAddListener(source, listener);
}
protected override void StartListening(object source)
{
(source as CarDealer).NewCarInfo += CarDealer_NewCarInfo;
}
void CarDealer_NewCarInfo(object sender, CarIInfoEventArgs e)
{
DeliverEvent(sender, e);
}
protected override void StopListening(object source)
{
(source as CarDealer).NewCarInfo -= CarDealer_NewCarInfo;
}
}
對(duì)于發(fā)布程序類CarDealer,不需要做任何修改,其實(shí)現(xiàn)代碼與前面相同。
2. 事件偵聽(tīng)器
偵聽(tīng)器需要改為實(shí)現(xiàn)IWeekEventListener接口。這個(gè)接口定義了ReceiveWeekEvent(),觸發(fā)事件時(shí),從弱事件管理器中調(diào)用這個(gè)方法。在該方法的實(shí)現(xiàn)代碼中,應(yīng)從觸發(fā)的事件中調(diào)用NewCarIsHere()方法。
public class Consumer : IWeakEventListener
{
private string name;
public Consumer(string name)
{
this.name = name;
}
public void NewCarIsHere(object sender, CarIInfoEventArgs e)
{
Console.WriteLine("{0}: car {1} is new", name, e.Car);
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
NewCarIsHere(sender, e as CarIInfoEventArgs);
return true;
}
}
在Main方法中,連接發(fā)布程序和偵聽(tīng)器,該連接現(xiàn)在使用WeekCarInfoEventManager類的AddListener()和RemoveListener()靜態(tài)方法。
static void Main(string[] args)
{
var dealer = new CarDealer();
var michael = new Consumer("Michael");
WeakCarInfoEventManager.AddListener(dealer, michael);
dealer.NewCar("Mercedes");
var nick = new Consumer("Nick");
WeakCarInfoEventManager.AddListener(dealer, nick);
dealer.NewCar("Ferrari");
WeakCarInfoEventManager.AddListener(dealer, michael);
}