C# 委托、事件,lamda表达式
http://www.cnblogs.com/android-blogs/p/5851002.html
注:看红字部分
1. 委托Delegate
C#中的Delegate对应于C中的指针,但是又有所不同C中的指针既可以指向方法,又可以指向变量,并且可以进行类型转换,
C中的指针实际上就是内存地址变量,他是可以直接操作内存的,通过内存地址直接访问变量,直接调用方法。
而C#中的Delegate是强类型的,也就是说在声明委托时就已经指定了该变量只能指向具有特定参数,以及返回值的方法。
    使用delegate就可以直接建立任何名称的委托类型,当进行系统编译时,系统就会自动生成此类型。您可以使用delegate void MyDelegate() 
方式建立一个委托类,并使用ILDASM.exe观察其成员。由ILDASM.exe 中可以看到,它继承了System.MulticastDelegate类,
并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。
Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法的。
| 
1 
2 
3 
4 
5 
6 
7 
8 | publicclassMyDelegate:MulticastDelegate      {          //同步调用委托方法          publicvirtualvoidInvoke();          //异步调用委托方法          publicvirtualIAsyncResult
 BeginInvoke(AsyncCallback callback,objectstate);          publicvirtualvoidEndInvoke(IAsyncResult
 result);      } | 
MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路广播委托,并拥有一个带有链接的委托列表,在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。
MulticastDelegate具有两个常用属性:Method、Target。其中Method 用于获取委托所表示的方法Target 用于获取当前调用的类实例。
1.1 委托的使用
当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。使用myDelegate.Invoke(string message),就能显式调用委托方法。但在实际的操作中,我们无须用到 Invoke 方法,而只要直接使用myDelegate(string message),就能调用委托方法。
无返回值的委托
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 | classProgram     {         delegatevoidMyDelegate(stringmessage);          publicclassExample         {             publicvoidMethod(stringmessage)             {                 MessageBox.Show(message);             }         }          staticvoidMain(string[]
 args)         {             Example
 example=newExample();             MyDelegate
 myDelegate=newMyDelegate(example.Method);             myDelegate("Hello
 World");             Console.ReadKey();         }     } | 
有返回值的委托
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 | classProgram     {         delegatestringMyDelegate(stringmessage);          publicclassExample         {             publicstringMethod(stringname)             {                 return"Hello
 "+
 name;             }         }          staticvoidMain(string[]
 args)         {             Example
 example=newExample();             //绑定委托方法             MyDelegate
 myDelegate=newMyDelegate(example.Method);             //调用委托,获取返回值             stringmessage
 = myDelegate("Leslie");             Console.WriteLine(message);             Console.ReadKey();         }     } | 
多路广播委托
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 | delegatedoubleMyDelegate(doublemessage);          publicclassPrice         {             publicdoubleOrdinary(doubleprice)             {                 doubleprice1
 = 0.95 * price;                 Console.WriteLine("Ordinary
 Price : "+price1);                 returnprice1;             }              publicdoubleFavourable(doubleprice)             {                 doubleprice1
 = 0.85 * price;                 Console.WriteLine("Favourable
 Price : "+
 price1);                 returnprice1;             }              staticvoidMain(string[]
 args)             {                 Price
 price = newPrice();                 //绑定Ordinary方法                 MyDelegate
 myDelegate = newMyDelegate(price.Ordinary);                 //绑定Favourable方法                 myDelegate
 += newMyDelegate(price.Favourable);                 //调用委托                 Console.WriteLine("Current
 Price : "+
 myDelegate(100));                 Console.ReadKey();             }         } | 
输出
1.2 委托的协变与逆变
前面已经说过,委托是强类型的方法指针,但是在面对具有继承关系类型的参数、或者返回值时,委托是如何处理的呢。
协变(返回值类型具有继承关系的方法)
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 | publicclassWorker      {.......}      publicclassManager:Worker      {.......}         classProgram      {          publicdelegateWorker
 GetWorkerHandler(intid);          //在
 Framework2.0 以上,委托 GetWorkerHandler 可绑定 GetWorker 与 GetManager 两个方法            publicstaticWorker
 GetWorker(intid)          {              Worker
 worker = newWorker();              returnworker;          }            publicstaticManager
 GetManager(intid)          {              Manager
 manager = newManager();              returnmanager;          }           staticvoidMain(string[]
 args)         {             GetWorkerHandler
 workerHandler = newGetWorkerHandler(GetWorker);             Worker
 worker=workerHandler(1);             GetWorkerHandler
 managerHandler = newGetWorkerHandler(GetManager);             Manager
 manager = managerHandler(2) asManager;             Console.ReadKey();         }      } | 
委托 GetWorkerHandler 可以绑定 GetWorker 与 GetManager 两个方法
逆变
委托逆变,是指委托方法的参数同样可以接收 “继承” 这个传统规则。像下面的例子,以 object 为参数的委托,可以接受任何 object 子类的对象作为参数。最后可以在处理方法中使用 is 对输入数据的类型进行判断,分别处理对不同的类型的对象。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 | classProgram     {         publicdelegatevoidHandler(objectobj);          publicstaticvoidGetMessage(objectmessage)         {             if(message
isstring)                 Console.WriteLine("His
 name is : "+
 message.ToString());             if(message
isint)                 Console.WriteLine("His
 age is : "+
 message.ToString());         }          staticvoidMain(string[]
 args)         {             Handler
 handler = newHandler(GetMessage);             handler(29);             Console.ReadKey();         }    } | 
注意:委托与其绑定方法的参数必须一至,即当 Handler 所输入的参数为 object 类型,其绑定方法 GetMessage 的参数也必须为 object 。否则,即使绑定方法的参数为 object 的子类,系统也无法辨认。
大家可能注意到了,这个委托方法GetMessage的实现不是那么优雅,于是泛型委托应运而生。
class Program { public delegate void Handler<T>(T obj); public static void GetWorkerWages(Worker worker) { Console.WriteLine("Worker's total wages is " + worker.Wages); } public static void GetManagerWages(Manager manager) { Console.WriteLine("Manager's total wages is "+manager.Wages); } static void Main(string[] args) { Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages); Worker worker = new Worker(); worker.Wages = 3000; workerHander(worker); Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages); Manager manager = new Manager(); manager.Wages = 4500; managerHandler(manager); Console.ReadKey(); } }
2. event事件的由来
事件是特殊的委托,他为委托提供了封装性,一方面允许从类的外部增加,删除绑定方法,另一方面又不允许从类的外部来触发委托所绑定了方法。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 | publicdelegatedoublePriceHandler();      publicclassPriceManager     {         publicPriceHandler
 GetPriceHandler;          //委托处理,当价格高于100元按8.8折计算,其他按原价计算         publicdoubleGetPrice()         {             if(GetPriceHandler.GetInvocationList().Count()
 > 0)             {                 if(GetPriceHandler()
 > 100)                     returnGetPriceHandler()*0.88;                 else                     returnGetPriceHandler();             }             return-1;         }     }      classProgram     {         staticvoidMain(string[]
 args)         {             PriceManager
 priceManager = newPriceManager();                          //调用priceManager的GetPrice方法获取价格             //直接调用委托的Invoke获取价格,两者进行比较             priceManager.GetPriceHandler
 = newPriceHandler(ComputerPrice);             Console.WriteLine(string.Format("GetPrice\n 
 Computer's price is {0}!",                 priceManager.GetPrice()));             Console.WriteLine(string.Format("Invoke\n 
 Computer's price is {0}!",                 priceManager.GetPriceHandler.Invoke()));                          Console.WriteLine();                          priceManager.GetPriceHandler
 = newPriceHandler(BookPrice);             Console.WriteLine(string.Format("GetPrice\n 
 Book's price is {0}!",                 priceManager.GetPrice()));             Console.WriteLine(string.Format("Invoke\n 
 Book's price is {0}!",                 priceManager.GetPriceHandler.Invoke()));                          Console.ReadKey();         }         //书本价格为98元         publicstaticdoubleBookPrice()         {             return98.0;         }         //计算机价格为8800元         publicstaticdoubleComputerPrice()         {             return8800.0;         }     } | 
以上代码实现了对于100元以上商品的的88折处理。一方面为了给GetPriceHandler绑定方法就必须将委托声明为public,但是一旦声明为public
就可以在类外部直接通过Invoke来调用该委托所绑定的方法,而产生我们不需要的结果。
当然我们可以将GetPriceHandler声明为private并且通过public 的addHandler,removeHandler来消除委托public的副作用,但是C#提供了更加优雅的方法:
那就是event关键字。
事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法,用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐的代码。
| 
1 
2 
3 
4 
5 | publicclassEventTest     {         publicdelegatevoidMyDelegate();         publiceventMyDelegate
 MyEvent;     } | 
观察事件的编译过程可知,在编译的时候,系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。
事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。
值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变量成员。
注意:在事件所处的对象之外,事件只能出现在+=,-=的左方。
public delegate void MyDelegate(string name);
 
     public class PersonManager
     {
         public event MyDelegate MyEvent;
 
         //执行事件
         public void Execute(string name)
         {
             if (MyEvent != null)
                 MyEvent(name);
         }
     }
 
     class Program
     {
         static void Main(string[] args)
         {
             PersonManager personManager = new PersonManager();
             //绑定事件处理方法
             personManager.MyEvent += new MyDelegate(GetName);
             personManager.Execute("Leslie");
             Console.ReadKey();
         }
 
         public static void GetName(string name)
         {
             Console.WriteLine("My name is " + name);
         }
     }
在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 | publicdelegatevoidMyDelegate(stringname);      publicclassPersonManager     {         publiceventMyDelegate
 MyEvent;         .........     }      classProgram     {         staticvoidMain(string[]
 args)         {             PersonManager
 personManager = newPersonManager();             //绑定事件处理方法             personManager.MyEvent
 += GetName;             .............         }          publicstaticvoidGetName(stringname)         {.........}    } | 
如果觉得编写 GetName 方法过于麻烦,你还可以使用匿名方法绑定事件的处理。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 | publicdelegatevoidMyDelegate(stringname);      publicclassPersonManager     {         publiceventMyDelegate
 MyEvent;          //执行事件         publicvoidExecute(stringname)         {             if(MyEvent
 != null)                 MyEvent(name);         }          staticvoidMain(string[]
 args)         {             PersonManager
 personManager = newPersonManager();             //使用匿名方法绑定事件的处理             personManager.MyEvent
 += delegate(stringname){                 Console.WriteLine("My
 name is "+name);             };             personManager.Execute("Leslie");             Console.ReadKey();         }     } | 
| 
1 |   | 
3. lambda表达式
在Framework 2.0 以前,声明委托的唯一方法是通过方法命名,从Framework 2.0 起,系统开始支持匿名方法。
通过匿名方法,可以直接把一段代码绑定给事件,因此减少了实例化委托所需的编码系统开销。
而在 Framework 3.0 开始,Lambda 表达式开始逐渐取代了匿名方法,作为编写内联代码的首选方式。总体来说,Lambda 表达式的作用是为了使用更简单的方式来编写匿名方法,彻底简化委托的使用方式。
使用匿名方法
| 
1 
2 
3 
4 
5 
6 
7 | staticvoidMain(string[]
 args)         {             Button
 btn = newButton();             btn.Click+=delegate(objectobj,EventArgs
 e){                 MessageBox.Show("Hello
 World !");             };         } | 
使用lambda表达式
| 
1 
2 
3 
4 
5 
6 
7 | staticvoidMain(string[]
 args)         {             Button
 btn = newButton();             btn.Click+=(objectobj,EventArgs
 e)=>{                 MessageBox.Show("Hello
 World !");             };         } | 
3.1常用泛型委托
public delegate bool Predicate<T>(T obj)
它是一个返回bool的泛型委托,能接受一个任意类型的对象作为参数。
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 | classProgram     {         staticvoidMain(string[]
 args)         {             List<Person>
 list = GetList();             //绑定查询条件             Predicate<Person>
 predicate = newPredicate<Person>(Match);             List<Person>
 result = list.FindAll(predicate);             Console.WriteLine(“Person
 count is:
 ” + result.Count);             Console.ReadKey();         }         //模拟源数据         staticList<Person>
 GetList()         {             varpersonList
 = newList<Person>();             varperson1
 = newPerson(1,"Leslie",29);             personList.Add(person1);             ........             returnpersonList;         }         //查询条件         staticboolMatch(Person
 person)         {             returnperson.Age
 <= 30;         }     }      publicclassPerson     {         publicPerson(intid,
stringname,
intage)         {             ID
 = id;             Name
 = name;             Age
 = age;         }          publicintID         {
get;
set;
 }         publicstringName         {
get;
set;
 }         publicintAge         {
get;
set;
 }     } | 
| 
1 |   | 
Action<T> 的使用方式与 Predicate<T> 相似,不同之处在于 Predicate<T> 返回值为 bool ,  Action<T> 的返回值为 void。
Action 支持0~16个参数,可以按需求任意使用。
public delegate void Action()
public delegate void Action<T1>(T1 obj1)
public delegate void Action<T1,T2> (T1 obj1, T2 obj2)
public delegate void Action<T1,T2,T3> (T1 obj1, T2 obj2,T3 obj3)
............
public delegate void Action<T1,T2,T3,......,T16> (T1 obj1, T2 obj2,T3 obj3,......,T16 obj16)
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 | staticvoidMain(string[]
 args)         {             Action<string>
 action=ShowMessage;             action("Hello
 World");             Console.ReadKey();         }          staticvoidShowMessage(stringmessage)         {             MessageBox.Show(message);         } | 
| 
1 |   | 
委托 Func 与 Action 相似,同样支持 0~16 个参数,不同之处在于Func 必须具有返回值
public delegate TResult Func<TResult>()
public delegate TResult Func<T1,TResult>(T1 obj1)
public delegate TResult Func<T1,T2,TResult>(T1 obj1,T2 obj2)
public delegate TResult Func<T1,T2,T3,TResult>(T1 obj1,T2 obj2,T3 obj3)
............
public delegate TResult Func<T1,T2,T3,......,T16,TResult>(T1 obj1,T2 obj2,T3 obj3,......,T16 obj16)
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 | staticvoidMain(string[]
 args)         {             Func<double,
bool,
double>
 func = Account;             doubleresult=func(1000,
true);             Console.WriteLine("Result
 is : "+result);             Console.ReadKey();         }          staticdoubleAccount(doublea,boolcondition)         {             if(condition)                 returna
 * 1.5;             else                 returna
 * 2;         } | 
3.2 lambda表达式
Lambda 的表达式的编写格式如下:
x=> x * 1.5
当中 “ => ” 是 Lambda 表达式的操作符,在左边用作定义一个参数列表,右边可以操作这些参数。
例子一, 先把 int x 设置 1000,通过 Action 把表达式定义为 x=x+500 ,最后通过 Invoke 激发委托。
static void Main(string[] args)
         {
             int x = 1000;
             Action action = () => x = x + 500;
             action.Invoke();
 
             Console.WriteLine("Result is : " + x);
             Console.ReadKey();
         }
例子二,通过 Action<int> 把表达式定义 x=x+500, 到最后输入参数1000,得到的结果与例子一相同。
注意,此处Lambda表达式定义的操作使用 { } 括弧包括在一起,里面可以包含一系列的操作。
static void Main(string[] args)
         {
             Action<int> action = (x) =>
             {
                 x = x + 500;
                 Console.WriteLine("Result is : " + x);
             };
             action.Invoke(1000);
             Console.ReadKey();
         }
 
                    
                     
                    
                 
                    
                




 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号