本帖最后由 bbyy 于 2016-6-20 14:59 编辑
【内容提要】本文讨论了C#含事件的类的设计方法和使用方法。通过一个实例详细解剖了事件、代理、事件处理的基本的概念。也许对初学C#的人员会有一定的帮助。
一、C#的事件处理涉及到的内容
1、用户事件参数的定义(从EventArgs派生)
2、delegate的定义
3、event的定义
4、事件唤醒方法即On方法
5、用户事件处理方法
编写含有事件的类实际上只需要前四项内容,最后一项是应用该类的用户来编写的。
如果无用户参数,则不需定义1和2两项,直接使用系统提供的EventArgs和EventHandler即可。这时从3开始定义:
事件的定义:
public event void EventHandler MyHandler;
On事件唤醒方法就是:
protected virtual void OnMyHandler ( EventArgs e )
{
if (MyHandler != null)
MyHandler( this, e );
}
本文要探讨的是完整的事件处理过程,所以事件参数自己定义。详见下例。
二、实例
我们通过一个实例来看如何实现事件处理的完整过程
要求:编写一个Person类
1、含有name字段用于存储名字,含有age字段存储年龄,同时设置两个属性Name和Age分别实现对name和age的获取和设置。
2、修改age字段后会触发一个事件AgeChanged,由用户决定修改age后如何操作,用户能够通过事件参数获得修改之前的age的值。
3、编写Person类的应用程序:一种是直接使用Person,另一种是从Person派生一个Student类,并分别给出相应的事件处理的方法
三、具体步骤
1、定义事件参数PersonEventArgs (如果你的类无需事件参数可不定义,直接用系统提供的EventArgs就可以了,本例中要求在事件处理方法中通过参数PersonEventArgs e 获得修改之前的年龄,所以必须定义新的事件参数)
public class PersonEventArgs:EventArgs
{
private int oldage; //修改之前的年龄
public int OldAge
{
get { return oldage; }
}
public PersonEventArgs( int age )
{
this.oldage = age;
}
}
2、定义代理PersonHandler
public delegate void PersonHandler(object sender, PersonEventArgs e);
如果没有用户自定义的事件参数,那么可以不定义用户自己的代理,直接使用System的EventHandler即可。
3、定义Person类(这是一个含事件的类),事件AgeChanged被定义在该类中(需要说明的是上述PersonEventArgs和PersonHandler都可以定义在Person类中)
public class Person
{
private string name; //姓名
private int age; //年龄
//定义事件:当年龄被改变之后触发
public event PersonHandler AgeChanged;
//这是事件唤醒方法:按dotnet规范被定义为protected virtual
protected virtual void OnAgeChanged( PersonEventArgs e )
{
if (AgeChanged != null)
AgeChanged( this, e );
}
//这是名字属性
public string Name
{
get { return name; }
set { name = value; }
}
//这是年龄属性:年龄改变时要触发AgeChanged事件,由于是改变后触发所以On放在了最后
public int Age
{
get { return age; }
set
{
if (age != value)
{
int age0 = age;
age = value;
OnAgeChanged(new PersonEventArgs(age0));
}
}
}
//这是年龄增加的方法:注意要触发AgeChanged事件
public void AddAge(int offset)
{
if (offset != 0)
{
int age0 = age;
age += offset;
OnAgeChanged( new PersonEventArgs(age0) );
}
}
public override string ToString()
{
return string.Format ("Name:{0}\tAge:{1}\t",name ,age );
}
public Person()
{
name = "";
age = 0;
}
public Person(string name,int age)
{
this.name = name;
this.age = age;
}
}
4、应用Person类
<方法一>直接使用Person类
class Program
{
static void Main(string[] args)
{
Person p = new Person("李四",20);
Console.WriteLine(p.ToString());
//向事件代理指定用户的事件处理方法
p.AgeChanged += new PersonHandler(Person_AgeChanged);
p.AddAge(60); //修改了年龄,要触发AgeChanged事件
Console.WriteLine(p.ToString());
Console.Read();
}
//这是用户的事件处理方法
protected static void Person_AgeChanged(object sender,PersonEventArgs e)
{
//用户在此添加代码实现用户要求
Console.WriteLine("修改前的年龄是:{0}",e.OldAge );
}
}
运行结果如下:
Name:李四 Age:20
修改前的年龄是:20
Name:李四 Age:80
注意:用这种方法使用Person不能用其OnAgeChange这个事件唤醒方法,因为它在Person中被定义为protected virtual。如果定义为public virtual 就可了,但是从.net 定义的事件看所有的事件唤醒On方法都是定义成protected virtual的。所以本文不讨论public virtual型的On事件唤醒方法。
<方法二>从Person派生新类:只使用其OnAgeChanged 而不用用户的事件处理方法,实际上就是用OnAgeChange作为用户的事件处理方法了。
设计一个类Student,增加一个字段年级grade,并设置一个属性Grade获取和设置grade的值。同样当修改学生的年龄时要触发AgeChanged事件。
public class Student:Person
{
private string grade; //班级
public string Grade
{
get { return grade; }
set { grade = value; }
}
public override string ToString()
{
return base.ToString()+ "Grade:" + grade ;
}
//由于不编写用户的单独的事件处理方法,所以直接使用On方法来实现用户的事件处理
protected override void OnAgeChanged( PersonEventArgs e )
{
//用户添加代码,实现用户要求
Console.WriteLine("修改了年龄:修改前的年龄是 {0},修改后的年龄是 {1}", e.OldAge, Age);
}
//这是另一个可能触发AgeChanged事件的方法:重新设置对象
public void Set(string name, int age, string grade)
{
Name = name;
Age = age; //特别注意:只有改变属性Age才可能触发AgeChanged事件。
this.grade = grade;
}
public Student(string name,int age,string grade):base(name ,age )
{
this.grade = grade;
}
}
测试程序如下:
using System;
class Program
{
static void Main(string[] args)
{
Student st = new Student("张三",50,"高二23班");
Console.WriteLine(st.ToString ());
st.Age = 60; //修改年龄,要触发AgeChanged事件
Console.WriteLine(st.ToString());
st.AddAge(30); //增加年龄,要触发AgeChanged事件
Console.WriteLine(st.ToString());
Console.Read();
}
}
运行结果如下:
Name:张三 Age:50 Grade:高二23班
修改了年龄:修改前的年龄是 50,修改后的年龄是 60
Name:张三 Age:60 Grade:高二23班
修改了年龄:修改前的年龄是 60,修改后的年龄是 90
Name:张三 Age:90 Grade:高二23班
注意:用户要想在年龄被修改后做点什么,那么就直接在Student的OnAgeChanged中进行相应的编码就可以了。
<方法三>从Person派生新类:不直接使用OnAgeChanged而用代理方法
例子同<方法二>
public class Student:Person
{
private string grade; //班级
public string Grade
{
get { return grade; }
set { grade = value; }
}
public override string ToString()
{
return base.ToString()+ "Grade:" + grade ;
}
//用户的事件处理方法
protected void Person_AgeChanged ( object sender , PersonEventArgs e)
{
//用户自己添加代码,实现用户的要求
Console.WriteLine("修改了年龄:修改前的年龄是 {0},修改后的年龄是 {1}", e.OldAge, Age);
}
public void Set(string name, int age, string grade)
{
Name = name;
Age = age;
this.grade = grade;
}
public Student(string name,int age,string grade):base(name ,age )
{
//使用用户的事件处理方法时必须使事件AgeChanged 指向用户事件处理方法
AgeChanged += new PersonHandler(Person_AgeChanged );
this.grade = grade;
}
}
}
测试程序同上,结果同上。
需要说明的是上述测试程序要想正确运行应该在每个类定义前加上一行:
using System;
四、总结
通过上述实例我们清楚的了解了C#事件的处理过程和如何设计含有事件的类及其使用方法。
|