再探委托与事件
在这里补充两个对委托运用得比较少的示例:
一、将委托作为参数传递,然后再调用;
二、使用委托调用内部类中的方法;
示例一:委托作为参数
委托作为参数.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace 委托作为参数
{
class 计算器
{
public delegate int delCalculate(int a, int b);
// 将委托作为参数
public void Fun(int a, int b, delCalculate cal)
{
if (cal != null)
{
Console.WriteLine(cal(a,b));
}
}
// 加法
public int Add(int a, int b)
{
return a + b;
}
// 减法
public int Reduce(int a, int b)
{
return a - b;
}
}
class Run
{
static void Main(string[] args)
{
计算器 js = new 计算器();
//调用加法
计算器.delCalculate d1 = new 计算器.delCalculate(js.Add);
js.Fun(20,10, d1); //将委托作为参数
//调用减法
js.Fun(20,10,new 计算器.delCalculate(js.Reduce));
}
}
}
using System.Collections.Generic;
using System.Text;
namespace 委托作为参数
{
class 计算器
{
public delegate int delCalculate(int a, int b);
// 将委托作为参数
public void Fun(int a, int b, delCalculate cal)
{
if (cal != null)
{
Console.WriteLine(cal(a,b));
}
}
// 加法
public int Add(int a, int b)
{
return a + b;
}
// 减法
public int Reduce(int a, int b)
{
return a - b;
}
}
class Run
{
static void Main(string[] args)
{
计算器 js = new 计算器();
//调用加法
计算器.delCalculate d1 = new 计算器.delCalculate(js.Add);
js.Fun(20,10, d1); //将委托作为参数
//调用减法
js.Fun(20,10,new 计算器.delCalculate(js.Reduce));
}
}
}
示例二:委托调用内部类的方法
委托调用内部类的方法.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace 委托调用内部类的方法
{
class Person
{
public delegate void myDelegate(string str);
public void Fun(string str)
{
Console.WriteLine(str);
}
//内部类的调用
public class InnerClass
{
public void SubFun(string str)
{
Console.WriteLine(str);
}
}
}
class Program
{
static void Main(string[] args)
{
Person p = new Person();
//内部委托的实例化(用类名调用)
Person.myDelegate pm1 = new Person.myDelegate(p.Fun);
pm1("内部委托调用的方法");
//内部类 InnerClass
Person.InnerClass pc = new Person.InnerClass();
Person.myDelegate pm2 = new Person.myDelegate(pc.SubFun);
pm2("内部委托调用内部类的方法");
}
}
}
using System.Collections.Generic;
using System.Text;
namespace 委托调用内部类的方法
{
class Person
{
public delegate void myDelegate(string str);
public void Fun(string str)
{
Console.WriteLine(str);
}
//内部类的调用
public class InnerClass
{
public void SubFun(string str)
{
Console.WriteLine(str);
}
}
}
class Program
{
static void Main(string[] args)
{
Person p = new Person();
//内部委托的实例化(用类名调用)
Person.myDelegate pm1 = new Person.myDelegate(p.Fun);
pm1("内部委托调用的方法");
//内部类 InnerClass
Person.InnerClass pc = new Person.InnerClass();
Person.myDelegate pm2 = new Person.myDelegate(pc.SubFun);
pm2("内部委托调用内部类的方法");
}
}
}
事件
由于MSDN含混不清的误导,很多人会认为:事件就是一种受限的委托。但实际上不是!
事件与委托的关系好比字段与属性的关系。
事件实际上是一种委托属性,只不过这个属性重载的不是赋值运算符,而是+=和-=运算符。
对于一个属性而言,对它的赋值和取值运算会被转换为两个方法的调用。
例如:obj.A 这个属性
obj.A = "aaa"; 将调用 set 访问器: set { _a = value; }
string a = obj.A; 将调用 get 访问器: get { return _a; }
而事件也是一样,假设有一个事件obj.E,则下面的表达式也等同于后面的形式:
obj.E += d; obj.add_E( d )
obj.E -= d; obj.remove_E( d )
事件与属性的共同点:
事件也可以被继承;
事件也可以被重写;
事件也可以是虚的或者抽象的(表现在add和remove方法是抽象的或者虚的);
理论上,事件的add和remove方法也可以有不同的访问权限。
事件与属性的不同点:
属性是必须实现的,事件则不必(或者说,事件是最早出现的自动属性);
与自动属性一样,事件也会创建一个字段来保存对应的委托实例,而这个字段与事件同名且是私有。在C#中,你可以访问到这个字段,因为它真的就是一个字段。
换言之,在C#中写代码时,如果是在类的内部使用事件,其实就是引用那个自动生成的私有字段(委托字段)。
而在类的外部使用事件,是不同于在类的内部使用事件(因为这个字段是私有的,所以在类的外部也就访问不到)。
event 本质上所做的工作应该还是通过 delegate 来实现的,或者至少原理相同。event 关键字只不过是让CLR给所定义的 delegate 对象进行一个封装。
event 是通过对 delegate 的限制来封装一部分本来就不应该暴露在外的行为,从而更符合面向对象的思想(封装的原则)。
附加:
1、事件其实是由三个方法add、remove和raise组成的。但C#编译器总是不会生成raise方法。
2、自动事件不同的编译器实现是不同的,例如VB的编译器生成的私有字段是(事件名)Event。
3、Web开发中的Control也是事件驱动的。
参考来源:
http://www.cnblogs.com/Ivony/archive/2009/09/10/1563876.html
http://www.cnblogs.com/jujusharp/archive/2008/11/01/CSharp-delegate-event-difference.html
推荐阅读:[叩响C#之门]写给初学者:自定义事件
作者: XuGang 网名:钢钢 |
出处: http://xugang.cnblogs.com |
声明: 本文版权归作者和博客园共有。转载时必须保留此段声明,且在文章页面明显位置给出原文连接地址! |