第8章:委托、Lambda表達(dá)式和事件

  • #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è)步驟。

  1. 首先必須定義要使用的委托,對(duì)于委托,定義它就是告訴編譯器這種類型的委托表示哪種類型的方法。
  2. 然后,必須創(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);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 人生苦多,快來(lái) Kotlin ,快速學(xué)習(xí)Kotlin! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,669評(píng)論 9 118
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,621評(píng)論 1 32
  • 寫在開(kāi)頭:本人打算開(kāi)始寫一個(gè)Kotlin系列的教程,一是使自己記憶和理解的更加深刻,二是可以分享給同樣想學(xué)習(xí)Kot...
    胡奚冰閱讀 1,408評(píng)論 0 6
  • 從爬蟲(chóng)必要的幾個(gè)基本需求來(lái)講: 1.抓取 py的urllib不一定去用,但是要學(xué),如果還沒(méi)用過(guò)的話。 比較好的替代...
    Python程序媛閱讀 598評(píng)論 0 7
  • 夢(mèng)里以為花間月 醒來(lái)原是滿地霜
    老去的牛糞閱讀 183評(píng)論 0 1

友情鏈接更多精彩內(nèi)容