1. NET委托及应用
1.1 .NET委托概念
OOP中具有相同属性的对象抽象后成为类型(class)。那么,具有相同属性的函数或方法(也称具有相同的函数签名):
返回类型相同
参数类型、参数顺序及参数个数相同
抽象后又是什么概念?例如,1到n之间每个数的平方后求和函数int SquareSum(int n)和立方后求和函数int CubeSum(int n),它们具有相同的函数签名:返回类型int、参数只有一个且是int类型。static private int SquareSum(int n)
{
int m = 0;
for (int k = 1; k <= n; k++)
{
m += k * k;
}
return m;
}static private int CubeSum(int n)
{
int m = 0;
for (int k = 1; k <= n; k++)
{
m += k * k * k;
}
return m;
}
这些相同属性的函数抽象,就是.NET提出的一个新的类型概念——委托,关键字为delegate。
1.2 .NET委托声明及特点
与C/C++/C#的函数声明相同,声名一个委托需要有:委托名、返回类型、参数及类型。例如,声明前面定义的两个函数的委托PowerSum如下:
public delegate int PowerSum(int n);特别地,一类通用的事件处理委托EventHandler声明如下:
public delegate void EventHandler(object sender, EventArgs e)显然,与类定义不同,委托声名不需要定义成员,它只起一个表示作用(delegate就是代表的意思)。此外,delegate也是类,其基类是MulticastDelegate,再上层类是Delegate,顶层类是object。
1.3 .NET委托应用描述
Microsoft .NET Framework 通过委托向外提供一种函数回调机制。——《框架设计(第2版)》Jeffrey Richeter
是一种类型安全的方法引用,可以把它看成一个类型安全的C函数指针。——《.NET组件编程》Juval Lowy
要把方法传送给其它方法时需要委托。与C函数指针不同,.NET委托是类型安全的。——《C#高级编程》Christian Nagel
从上面名著的描述可以看出,.NET委托的主要用途是三个:1)函数回调;2)传递方法;3)事件机制。
1.4 .NET委托举例1:传递方法
委托作为方法传递时,有两种方式。第一种,直接传递方法,这种方式称为委托推断;第二种,创建委托对象后传递,这种方式是常规方式。应用前面定义的委托,现定义一个调用方法的函数:int GetPowerSum(PowerSum ps)如下,该函数用于计算1到10的指数和。
static private int GetPowerSum(PowerSum ps)
{
return ps(10);
}采用委托推断方式调用代码如下:
int p2 = GetPowerSum(SquareSum);
int p3 = GetPowerSum(CubeSum);采用创建委托对象方式调用代码如下:
PowerSum ps2 = new PowerSum(SquareSum);
PowerSum ps3 = new PowerSum(CubeSum);
p2 = ps2(10);
p3 = ps3(10);
1.5 .NET委托举例2:函数回调
最常见的回调应用之一,是计时器到点时调用的函数。涉及到的类型如下(.NET有三个计时器类型,这个是线程名称空间System.Threading里的Timer):
public sealed Timer(TimerCallBack callback, object state, int dueTime, int period);
public delegate void TimeCallBack(object state);
Timer类型中,callback是一个委托TimerCallBack的对象;state是调用时的状态参数,可以灵活应用;dueTime是计时器开始计时的等待时间;period是计时周期,每完成一个周期就调用方法callback
回调函数CalllBack的委托定义表明,计时器类Timer到点时回调的函数不能有返回类型,但必须有一个参数object型的参数。注意,此处委托的所谓逆变不能用了
现定义一个到点回调函数,即到点就输出字符串信息如下:
static private void TimeClick(object state)
{
Console.WriteLine("time click 500ms");
}那么500ms报时的计时器应用代码如下:
System.Threading.TimerCallback callBack = new System.Threading.TimerCallback(TimeClick);
System.Threading.Timer timer = new System.Threading.Timer(callBack, null, 0, 500);由于回调函数比较简单,可以使用匿名委托,代码如下
System.Threading.TimerCallback callBack = new System.Threading.TimerCallback
(
delegate(object state)
{
Console.WriteLine("time click 500ms");
}
);
System.Threading.timer = new System.Threading.Timer(callBack, null, 0, 500);
2 .NET事件及应用
2.1 .NET事件概念
一个如何获得另一个对象发生某个事件的通知?VB和C#中常用的方法如下
VB按钮(Command)点击事件:Sub Command1_Click()
C#按钮(Button)点击事件:void button1_Click(object sender, EventArgs e)
这表明,事件是一种信号机制,对象在发生某种活动时自动发出通知,是对象定义的外发消息接口。其它对象若对事件感兴趣,则为该事件注册一个事件处理程序。事件发生时,所有注册在该事件上的处理程序都会被调用。
发布事件的对象称为发布者(publisher)或事件源,发布事件也称为激发(fire)事件。关注事件的对象称为事件接收器(sinker)或订阅者(subscriber),订阅事件也称为注册事件方法。发布者调用订阅者的注册方法。.NET事件模型建立在委托机制之上,支持事件定义、发布、订阅、和拆除。
2.2 设计.NET事件5个步骤
定义参数类型:从类型EventArgs派生出满足要求的事件参数类
定义事件处理者委托:与第1)步相关,该步一般被泛型委托取代了
定义事件成员:在自定义类中,由事件处理者委托定义一个或多个事件成员
激发事件:在自定义类的引发事件方法中,通知所有事件订阅者
订阅事件:其它对象注册事件处理程序
需要指出,上述第3、4、5步必须存在,第1、2步可适当省略:
第2步可省。如果采用标准事件处理者委托类型:void EventHandler(object sender, EventArgs e),那么只需要第1步给出事件参数,然后使用泛型委托:EventHandler<T>即可定义类的事件成员了。其中,T就是事件参数类型
如果没有自定义事件参数,可以省略第1、2步,直接用EventHandler定义类的事件成员
2.3 事件设计举例
编写一个统计按键次数的键盘侦听类TKeyListen
TKeyListen可以发布侦听到的击键次数,并检查返回参数值
注册事件的对象可以终止侦听循环
第1步:定义事件参数类
public class KeyEventArgs : EventArgs
{
private int m_KeyCount;
private bool m_Stop = false;
public KeyEventArgs(int keyCount) // 发布事件时给出按键计数值
{
m_KeyCount = keyCount;
}
public int KeyCount
{
get { return m_KeyCount; }
}
public bool Stop
{
get { return m_Stop; }
set { m_Stop = value; } // 事件订阅者可以修改
}
}
第2步:声明事件处理者委托
public delegate void KeyEventHandler(object sender, KeyEventArgs e);实际使用时,除非上述委托有其它用途,一般使用泛性委托EventHandler<T>取代,其中T就是事件参数类型。
第3、4步:定义类事件成员、激发(发布)事件
<!--[if !ppt]-->public class TKeyListen
{
// public event KeyEventHandler KeyPress; // 第3步:定义事件成员
public event EventHandler<KeyEventArgs> KeyPress; // 第3步:泛型委托实现
public void Listen()
{
Console.WriteLine("Please press key.");
int keyCount = 0;
while (true) // 使用循环监听击键动作
{
ConsoleKeyInfo key = Console.ReadKey();
keyCount++;
if (KeyPress != null) // 如果存在订阅者,即:委托链非空
{
KeyEventArgs e = new KeyEventArgs(keyCount);
KeyPress(this, e); // 第4步:激发事件,通知所有订阅者
if (e.Stop) // 判断事件返回参数
{
break;
}
}
}
}
}上述代码包含了事件实现的第3、4步。其中循环代码while(true)包含了发布(激发)事件,特别说明如下:
必须判断委托链(订阅者链)是否空,即是否存在事件的订阅者:if (KeyPress != null)。如果没有事件订阅者,直接发布事件KeyPress(this,e),系统将抛出异常“未将对象引用设置到对象实例上”
激发或发布事件时,第一个参数是对象自己(this):KeyPress(this, e)
KeyPress(this,e)实际执行过程:遍历事件订阅者链,执行每个订阅事件方法,这些方法具有与KeyPress相同的委托类型
可以判断事件返回参数,即订阅者可以修改参数e.Stop,发布者检测该参数。如果有多个订阅者,上述代码只获得最后一个订阅事件处理方法给定的参数。如果要判断每个订阅方法的参数,必须使用委托的GetInvocationList()方法,逐个获得返回参数。
第5步:订阅事件
static void Main(string[] args)
{
TKeyListen demo = new TKeyListen();
demo.KeyPress += CountKey; // 订阅事件,使用委托推断方式
demo.Listen();
}
static void CountKey(object sender, KeyEventArgs e) // 事件处理方法
{
Console.WriteLine("Press count: " + e.KeyCount);
if (e.KeyCount == 5) e.Stop = true; // 5次后停止
}注意,上述代码中,事件处理方法CountKey必须与事件处理者委托或泛型委托一致。此外,需要说明如下:
订阅事件操作符为:+=,移除订阅操作为-=
建立委托对象订阅方式:demo.KeyPress += new KeyEventHandler(CountKey);
可以多次订阅,从而产生事件链:demo.KeyPress += delegate() {…}
小结:委托的主要用途是方法调用、函数回调和事件,而事件重要用于外发消息。
3 软件项目开发浅谈
3.1 开发文档
即使是小型软件项目,也至少需要包括如下开发文档:
技术文档:需求分析、系统运行环境、系统运行配置、库表设计、算法描述等
测试文档:单元/集成测试、验收测试
3.2 后期维护
长期维护准备:维护期限一般体现在合同中,一般3~5年
代码与文档维护:增加维护文档,记录所有的维护情况
3.3 技术积累
技术侧重:工具与语言、B/S与C/S模式
最新技术:例如:RIA中的SilverLight与Flex
行业知识:应用领域的专业知识
上面主要是小型、个人最多不过3人的软件项目,不涉及到大型的、公司里运作的项目开发方式。
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/4_webprogram/asp.net/asp_netshl/2008102/147320_2.html