C#委托、事件,lamda表达式

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#委托、事件,lamda表达式
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 8public class MyDelegate:MulticastDelegate
{
//同步调⽤委托⽅法
public virtual void Invoke();
//异步调⽤委托⽅法
public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state); public virtual void EndInvoke(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 20class Program
{
delegate void MyDelegate(string message);
public class Example
{
public void Method(string message)
{
MessageBox.Show(message);
}
}
static void Main(string[] args)
{
Example example=new Example();
MyDelegate myDelegate=new MyDelegate(example.Method); myDelegate("Hello World");
Console.ReadKey();
}
}
有返回值的委托1
2class Program
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23class Program
{
delegate string MyDelegate(string message);
public class Example
{
public string Method(string name)
{
return"Hello "+ name;
}
}
static void Main(string[] args)
{
Example example=new Example();
//绑定委托⽅法
MyDelegate myDelegate=new MyDelegate(example.Method); //调⽤委托,获取返回值
string message = 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 30delegate double MyDelegate(double message);
public class Price
{
public double Ordinary(double price)
{
double price1 = 0.95 * price;
Console.WriteLine("Ordinary Price : "+price1);
return price1;
}
public double Favourable(double price)
{
double price1 = 0.85 * price;
Console.WriteLine("Favourable Price : "+ price1);
return price1;
}
static void Main(string[] args)
{
Price price = new Price();
//绑定Ordinary⽅法
MyDelegate myDelegate = new MyDelegate(price.Ordinary); //绑定Favourable⽅法
myDelegate += new MyDelegate(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 30public class Worker
{.......}
public class Manager:Worker
{.......}
class Program
{
public delegate Worker GetWorkerHandler(int id);
//在 Framework2.0 以上,委托 GetWorkerHandler 可绑定 GetWorker 与 GetManager 两个⽅法 public static Worker GetWorker(int id)
{
Worker worker = new Worker();
return worker;
}
public static Manager GetManager(int id)
{
Manager manager = new Manager();
return manager;
}
static void Main(string[] args)
{
GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);
Worker worker=workerHandler(1);
GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager);
Manager manager = managerHandler(2) as Manager;
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 19class Program
{
public delegate void Handler(object obj);
public static void GetMessage(object message)
{
if(message is string)
Console.WriteLine("His name is : "+ message.ToString()); if(message is int)
Console.WriteLine("His age is : "+ message.ToString()); }
static void Main(string[] args)
{
Handler handler = new Handler(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 52public delegate double PriceHandler();
public class PriceManager
{
public PriceHandler GetPriceHandler;
//委托处理,当价格⾼于100元按8.8折计算,其他按原价计算
public double GetPrice()
{
if(GetPriceHandler.GetInvocationList().Count() > 0)
{
if(GetPriceHandler() > 100)
return GetPriceHandler()*0.88;
else
return GetPriceHandler();
}
return-1;
}
}
class Program
{
static void Main(string[] args)
{
PriceManager priceManager = new PriceManager();
//调⽤priceManager的GetPrice⽅法获取价格
//直接调⽤委托的Invoke获取价格,两者进⾏⽐较
priceManager.GetPriceHandler = new PriceHandler(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 = new PriceHandler(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元
public static double BookPrice()
{
return98.0;
}
//计算机价格为8800元
public static double ComputerPrice()
{
return8800.0;
}
}
54
55
以上代码实现了对于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 5public class EventTest
{
public delegate void MyDelegate(); public event MyDelegate 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 21public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
.........
}
class Program
{
static void Main(string[] args)
{
PersonManager personManager = new PersonManager(); //绑定事件处理⽅法
personManager.MyEvent += GetName;
.............
}
public static void GetName(string name)
{.........}
}
如果觉得编写 GetName ⽅法过于⿇烦,你还可以使⽤匿名⽅法绑定事件的处理。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24public delegate void MyDelegate(string name);
public class PersonManager
{
public event MyDelegate MyEvent;
//执⾏事件
public void Execute(string name)
{
if(MyEvent != null)
MyEvent(name);
}
static void Main(string[] args)
{
PersonManager personManager = new PersonManager(); //使⽤匿名⽅法绑定事件的处理
personManager.MyEvent += delegate(string name){
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 7static void Main(string[] args)
{
Button btn = new Button();
btn.Click+=delegate(object obj,EventArgs e){ MessageBox.Show("Hello World !");
};
}
使⽤lambda表达式
1 2 3 4 5 6 7static void Main(string[] args)
{
Button btn = new Button();
btn.Click+=(object obj,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 36class Program
{
static void Main(string[] args)
{
List<Person> list = GetList();
//绑定查询条件
Predicate<Person> predicate = new Predicate<Person>(Match); List<Person> result = list.FindAll(predicate);
Console.WriteLine(“Person count is: ” + result.Count);
Console.ReadKey();
}
//模拟源数据
static List<Person> GetList()
{
var personList = new List<Person>();
var person1 = new Person(1,"Leslie",29);
personList.Add(person1);
........
return personList;
}
//查询条件
static bool Match(Person person)
{
return person.Age <= 30;
}
}
public class Person
{
public Person(int id, string name, int age)
{
ID = id;
Name = name;
Age = age;
}
public int ID
{ get; set; }
37 38 39 40 41 42 43 public string Name { get; set; }
public int Age
{ 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 11static void Main(string[] args)
{
Action<string> action=ShowMessage; action("Hello World");
Console.ReadKey();
}
static void ShowMessage(string message) {
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 15static void Main(string[] args)
{
Func<double, bool, double> func = Account; double result=func(1000, true);
Console.WriteLine("Result is : "+result);
Console.ReadKey();
}
static double Account(double a,bool condition) {
if(condition)
return a * 1.5;
else
return a * 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();
}。

相关文档
最新文档