------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------
委托和事件作为winform,ASP.NET等不论是B/S,还是C/S开发的应用中是司空见惯的存在,我们初学者只有很好的掌握这些理论基础,才能在后续的学习中灵活且高效的运用这些语言特性。所以现将笔记整理如下。
一、委托
委托是用来处理需用函数指针来处理的情况的
委托是完全面向对象的,是类型安全的
委托是可保存对方法的引用的类。与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调
委托声明定义一个从 System.Delegate 类派生的类。委托实例封装了一个调用列表,该列表列出了一个或多个方法,每个方法称为一个可调用实体。对于实例方法,可调用实体由该方法和一个相关联的实例组成。下面给出委托的实例化和调用示例。
using System;
namespace CSharpPractice.Delegate
{
delegate void D(int[] A);
class ArraySort
{
public static void DisplayMatrix(int[] A)
{ //打印矩阵
foreach (int i in A) Console.Write("{0,5} ", i);
Console.WriteLine();
}
public static void GeneralSort(int[] A, D sort)
{
sort(A);
Console.WriteLine("升序数组: ");
foreach (int i in A) Console.Write("{0,5} ", i);
Console.WriteLine();
}
public static void BubbleSort(int[] A) //冒泡算法
{
int i, t;
int N = A.Length; //获取数组A的长度N
for (int loop = 1; loop <= N - 1; loop++)//外循环进行N-1轮比较
{
for (i = 0; i <= N - 1 - loop; i++) //内循环两两比较,大数下沉
if (A[i] > A[i + 1])
{
t = A[i];
A[i] = A[i + 1];
A[i + 1] = t;
}
}
}
public static void SelectSort(int[] A) //选择算法
{
int i, t, MinI;
int N = A.Length; //获取数组A的长度N
for (int loop = 0; loop <= N - 2; loop++) //外循环进行N-1轮比较
{
MinI = loop;
for (i = loop; i <= N - 1; i++) //内循环中在无序数中找最小值
if (A[i] < A[MinI]) MinI = i;
//最小值与无序数中的第一个元素交换
t = A[loop];
A[loop] = A[MinI];
A[MinI] = t;
}
}
static void Main()
{
int[] A = new int[10];
Random rNum = new Random();
//数组A赋值(0~100之间的随机数)
for (int i = 0; i < A.Length; i++) A[i] = rNum.Next(100);
Console.WriteLine("原始数组: ");
DisplayMatrix(A);
D d1 = new D(ArraySort.BubbleSort);//创建委托实例,指向冒泡算法
Console.Write("冒泡算法---");
GeneralSort(A, d1);
D d2 = new D(ArraySort.SelectSort);//创建委托实例,指向选择算法
Console.Write("选择算法---");
GeneralSort(A, d2);
Console.ReadKey();
}
}
}
2.匿名方法委托
无需先声明类或结构以及与委托匹配的方法,而是在创建委托的实例时,直接声明与委托匹配的方法的代码块(匿名方法),匿名方法委托例示。
using System;
namespace CSharpPractice.Delegate
{
// 声明委托
delegate void Printer(string s);
class TestClass
{
// 与命名委托相关的方法:
static void DoWork(string k)
{
Console.WriteLine(k);
}
static void Main()
{
// 使用匿名方法实例化delegate类:
Printer p = delegate(string j)
{
Console.WriteLine(j);
};
// 匿名delegate调用结果:
p("使用匿名方法的委托的调用.");
// 使用"DoWork"方法对delegate实例化:
p = new Printer(TestClass.DoWork);
// 传统delegate调用结果:
p("使用命名方法的委托的调用.");
Console.ReadLine();
}
}
}
3.多播委托
委托也可以包含多个方法,这种委托称为多播委托
如果调用多播委托实例,则按顺序依次调用多播委托实例封装的调用列表中的多个方法
声明多播委托时,其返回类型必须为void,因为无法处理多次调用的返回值,而且不能带输出参数(但可以带引用参数)
多播委托通过 + 或 += 向多播委托实例封装的调用列表中添加方法;通过 – 或 -= 从多播委托实例封装的调用列表中删除方法。
using System;
namespace CSharpPractice.Delegate
{
delegate void D(int x);
class C
{
public static void M1(int i)
{
Console.WriteLine("C.M1: " + i);
}
public static void M2(int i)
{
Console.WriteLine("C.M2: " + i);
}
public void M3(int i)
{
Console.WriteLine("C.M3: " + i);
}
}
class Test
{
static void Main()
{
D cd1 = new D(C.M1);
cd1(-1); // call M1
D cd2 = new D(C.M2);
cd2(-2); // call M2
D cd3 = cd1 + cd2;
cd3(10); // call M1 then M2
cd3 += cd1; cd3(20); // call M1, M2, then M1
C c = new C();
D cd4 = new D(c.M3);
cd3 += cd4; cd3(30); // call M1, M2, M1, then M3
cd3 -= cd1; // remove last M1
cd3(40); // call M1, M2, then M3
cd3 -= cd4; cd3(50); // call M1 then M2
cd3 -= cd2; cd3(60); // call M1
cd3 -= cd2; // impossible removal is benign
cd3(60); // call M1
cd3 -= cd1; // invocation list is empty so cd3 is null
// cd3(70); // System.NullReferenceException thrown
cd3 -= cd1; // impossible removal is benign
Console.ReadLine();
}
}
}
4.委托的兼容性
D和M的参数数目相同,且各自对应参数具有相同的ref或out修饰符;(D指委托,M指方法)
对于每个ref或out参数,D中的参数类型与M中的参数类型相同。
存在从M的返回类型到D的返回类型的标识或隐式引用转换。即允许方法具有的派生返回类型比委托中定义的更多(协变)。
每一个值参数(没有 ref 或 out 修饰符的参数)都存在从D中的参数类型到M中的对应参数类型的标识或隐式引用转换。允许方法具有的派生参数类型比委托类型中的更少(逆变)
using System;
namespace CSharpPractice.Delegate
{
class Mammals
{
//...
}
class Dogs : Mammals
{
//...
}
class Program
{ // 定义委托
public delegate Mammals HandlerMethod();
public delegate void HandlerMethod1(Mammals m);
public delegate void HandlerMethod2(Dogs d);
public static Mammals FirstHandler()
{
Console.WriteLine("first handler");
return null;
}
public static Dogs SecondHandler()
{
Console.WriteLine("second handler");
return null;
}
public static void ThirdHandler(Mammals m)
{
Console.WriteLine("third handler");
}
static void Main()
{
HandlerMethod handler1 = FirstHandler;
handler1();
//协变
HandlerMethod handler2 = SecondHandler;
handler2();
Mammals m = new Mammals();
HandlerMethod1 handler11 = ThirdHandler;
handler11(m);
//逆变
Dogs d = new Dogs();
HandlerMethod2 handler22 = ThirdHandler;
handler22(d);
Console.ReadKey();
}
}
}
二、事件
类或对象可以通过事件(event)向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为“发行者”(生产者),接收(或处理)事件的类称为“订户”(消费者)。
事件是一种使对象或类能够提供通知的成员。客户端可以通过提供事件处理程序(event handler)为相应的事件添加可执行代码。
事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。
2.事件特点
发行者确定何时引发事件,订户确定执行何种操作来响应该事件
一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件
没有订户的事件永远不会被调用
事件通常用于通知用户操作,例如,图形用户界面中的按钮单击或菜单选择操作
如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序
可以利用事件同步线程
在 .NET Framework 类库中,事件是基于 EventHandler 委托和 EventArgs 基类的
3.事件处理机制
事件实际上是委托的一种特殊形式。C# 使用一种委托模型来实现事件。事件模型分为事件生产者和事件消费者,其处理机制大致可以分为下列4步
在事件生产者类中声明一个事件成员,即某种事件处理委托(简称为事件委托)的实例(多播事件委托实例);
在事件消费者类中声明与事件委托相匹配的事件处理方法;
通过“+=”向多播事件委托实例封装的调用列表中添加事件处理方法,或通过“-=”从多播事件委托实例封装的调用列表中删除事件处理方法;
在事件生产者类中添加有关发生事件的代码,即当满足某种条件时(发生事件),则调用委托,即调用多播事件委托实例封装的调用列表中添加的事件处理方法。如果没有订阅,即事件实例为Null,则不作任何处理。
下面给出实现事件步骤的代码。算是对初步掌握事件一个总结吧。
<span style="font-size:18px;">using System;
using System.Collections;
namespace CSharpPractice.Event
{
//步骤1:声明提供事件数据的类。
public class NameListEventArgs : EventArgs
{
public string Name { get; set; }
public int Count { get; set; }
public NameListEventArgs(string name, int count)
{
Name = name;
Count = count;
}
}
//步骤2:声明事件处理委托。
public delegate void NameListEventHandler(object source, NameListEventArgs args);
//步骤3:声明引发事件的类(事件生产类)。
public class NameList
{
ArrayList list;
//步骤4:在事件生产类中,声明事件。
public event NameListEventHandler nameListEvent;
public NameList()
{
list = new ArrayList();
}
public void Add(string Name)
{
list.Add(Name);
//步骤5:在事件生产类中,实现产生事件的代码。
if (nameListEvent != null)
{
nameListEvent(this, new NameListEventArgs(Name, list.Count));
}
}
}
//步骤6:声明处理事件的类(事件消费类)。
public class EventDemo
{
//步骤7:在事件消费类中,声明事件处理方法。
public static void Method1(object source, NameListEventArgs args)
{
Console.WriteLine("列表中增加了项目:{0}", args.Name);
}
//步骤7:在事件消费类中,声明事件处理方法。
public static void Method2(object source, NameListEventArgs args)
{
Console.WriteLine("列表中的项目数:{0}", args.Count);
}
public static void Main()
{
NameList nl = new NameList();
//步骤8:在事件消费类中,订阅或取消事件。
nl.nameListEvent += new NameListEventHandler(EventDemo.Method1);
nl.nameListEvent += new NameListEventHandler(EventDemo.Method2);
nl.Add("张三");
nl.Add("李四");
nl.Add("王五");
Console.ReadLine();
}
}
}</span>
写了这么久,好累啊,该出去走走了,放松放松。。
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------