一、委托
1.委托其本质是一个密封类,通俗易懂的理解就是委托可以将方法像参数一样进行自由传递(委托不可以定义在接口中),委托包含的只是方法的细节(也就是方法的签名);
2.可以将委托当作类来理解,当使用类时,我们首先要定义一个类,再实例化一个类,即创建类的一个对象。同样,使用委托也需要经过这样的过程。定义委托就是告诉编译器该委托代表了哪种类型的方法(通过方法签名确定),然后创建该委托的一个或多个实例;
3.理解委托的一种好方式是把委托当作给方法特征(签名)指定一个名称,也就是说任何一个方法(实例方法或静态方法),只要他的特征满足委托的定义,从理论上来说就可以通过这个委托来引用这个方法;
4.定义一个委托其实质就是定义一个新类(密封类)(委托是派生于基类System.Delegate的一个密封类),所以可以在定义类的任何地方定义委托,甚至可以在名称空间中把委托定义为顶层对象;
5.类创建的实例我们称之为“对象”,但是委托创建的实例,我们任然称之为委托;
6.委托在语法上总是带有一个参数构造函数,这个参数就是委托引用的方法,这个方法必须匹配最初定义委托时的签名;
7.委托既可以引用静态方法,又可以引用实例方法,只要方法的签名匹配委托的特征即可;
8.委托可以包含多个方法,这种委托称为委托链(多播委托);
9.委托可以识别+和+=运算符,他可以将方法添加到委托列表中,同样也可以识别-和-=运算符,他可以将方法从委托列表中移除,这是因为重写了+=和-=运算符;
10.有了委托就可以将方法当着参数来进行传递;
namespace EventDel
{
//定义一个委托。委托可以定义在任意位置,包括定义在类的外面
public delegate int AddDelegate(int a, int b);
public class Program
{
static void Main(string[] args)
{
//实例化一个委托
AddDelegate addDel = new AddDelegate(Decrease);
//委托调用。
Console.WriteLine(addDel(2, 5));
//以下两种方式都可以
//addDel += new AddDelegate(Add);
addDel += Add;
//打印出-3和7,因为他依次执行了Decrease方法和Add方法
Console.WriteLine(addDel(2, 5));
//返回7,因为他将先执行的Decrease方法的返回值覆盖掉了
int iResult = addDel(2, 5);
//返回每一个委托方法的返回值
Delegate[] delegateList = addDel.GetInvocationList();
foreach (var list in delegateList)
{
object result = list.DynamicInvoke(8, 9);
Console.WriteLine(result);
}
//将该委托从该委托列表移除
addDel -= new AddDelegate(Decrease);
Console.WriteLine(addDel(2, 5));
//将方法当着参数传递给委托
int iResultTest = Test.Sum(Add);
Console.WriteLine(iResultTest);
Console.ReadLine();
}
public static int Add(int a, int b)
{
return a + b;
}
public static int Decrease(int a, int b)
{
return a - b;
}
}
public class Test
{
public static int Sum(AddDelegate addDel)
{
return addDel(7,9);
}
}
}
二、事件
1.C#中事件经常被用到,一提到事件必然会和委托有联系。例如,Button按钮的Click事件,我们不需要定义对应的委托,那是因为微软将他封装好了,我们只需要调用即可,但是自定义事件就必须要定义相应的委托;
2.C#中的事件遵循“发布——订阅”的设计模式,在这种模式中,一个类公布能够出现的事件,然后任意数量的类都可以预定这个事件,一旦事件产生,运行环境就负责通知每个订阅用户,告诉他们事件已经发生了,然后去执行对应的方法;
3.事件处理一般有一下几个步骤:
(1)、声明委托;
(2)、声明委托事件;
(3)、添加事件的触发方法,事件只能又事件发布者进行触发;
(4)、添加事件的响应方法;
(5)、将指定的事件响应方法邦定到要处理的事件上(订阅事件);
(6)、用户信息操作,触发事件(调用事件的触发方法);
(7)、通过事件委托的回调,执行我们需要的事件处理方法。
/// <summary>
/// 事件参数类
/// </summary>
public class TestEventArgs:EventArgs
{
#region Construtor
public TestEventArgs(string content)
{
this.strContent = content;
}
#endregion
#region Properties
public string strContent
{
get;
set;
}
#endregion
}
/// <summary>
/// 事件发生类
/// </summary>
public class DefineEvent
{
#region Fields
//定义委托
public delegate void TestEventHandler(object obj, TestEventArgs e);
//定义事件
public event TestEventHandler OnReceive;
#endregion
#region Constructor
public DefineEvent()
{
}
#endregion
#region Methods
public void StartEvent(TestEventArgs e)
{
//触发事件 只有事件的发布者才能触发事件
OnReceive(this, e);
}
#endregion
}
/// <summary>
/// 订阅事件类
/// </summary>
public class ReceiveEvent
{
#region Contructor
public ReceiveEvent(DefineEvent definEvent)
{
//订阅事件,只要满足该委托的方法都可以
//definEvent.OnReceive += new DefineEvent.TestEventHandler(AnotherReceiveEvent.OnReceiveData);
definEvent.OnReceive += new DefineEvent.TestEventHandler(OnReceiveData);
}
#endregion
#region Methods
public void OnReceiveData(object obj, TestEventArgs e)
{
Console.WriteLine("您输入的是:" + e.strContent);
}
#endregion
}
/// <summary>
/// 另一个订阅事件类
/// </summary>
public class AnotherReceiveEvent
{
public AnotherReceiveEvent(DefineEvent definEvent)
{
definEvent.OnReceive += new DefineEvent.TestEventHandler(AnotherReceiveEvent.OnReceiveData);
}
public static void OnReceiveData(object obj, TestEventArgs e)
{
Console.WriteLine("您输入的内容是:" + e.strContent);
}
}
public class Program
{
static void Main(string[] args)
{
string strContent = Console.ReadLine();
if (!string.IsNullOrEmpty(strContent))
{
TestEventArgs testEventArgs = new TestEventArgs(strContent);
DefineEvent defineEvent = new DefineEvent();
//同一个事件可以被多个订户订阅,此处有两个类订阅了该事件
ReceiveEvent receiveEvent = new ReceiveEvent(defineEvent);
AnotherReceiveEvent test = new AnotherReceiveEvent(defineEvent);
defineEvent.StartEvent(testEventArgs);
}
Console.ReadLine();
}
}
三、委托和事件的关系
1.委托的本质是一个密封类,因此他可以像类一样在很多地方定义(如命名空间内、类内等)。而事件被编译成一个委托类型的变量,因此他只能定义在一个类里面;
2.委托是事件的基础,可以把事件看成是委托的一种代理;
3.事件只能由发布者进行触发,外部不能直接触发事件。