C#学习笔记 事件

事件(event)是在委托的基础上实现的一种“通知机制”。比如,当一个按钮被点击,就是一个事件,事件的发生可以通知相关的程序进行处理。事件相当于其他语言中的回调函数,或事件监听类。
对于Button类来说,Click是其中一个特殊的属性,它就代表一个事件,它的类型是EventHandler委托,是的,关于事件有几个关键点:

  • 在一个类中定义一个事件,事件的类型是一个委托类型
  • 在外面用+=来注册这个事件,即将一个外部方法关联上了
  • 在事件源所在的类中,在一定条件下(即事件发生了)来调用这个委托,实际上是调用了外部方法,即相当于通知到外部了。在调用时,还传递了事件发生时的具体详情。

自定义事件

事件机制的工作过程如下:
关心某事件的对象向能发出事件的对象进行事件处理程序的注册。当事件发生时,会调用所有注册的事件处理程序。事件处理程序要用委托来表示。可以认为,事件就是委托实例,只不过为了便于应用,C#在委的基础上进行了一些增强,在使用方式上进行了一些限定。下面是自定义事件的一些步骤:

1.事件的声明

事件是类、结构及接口的成员,声明格式如下:

	修饰符 event 委托类型名 事件名;

其中修饰符可以是访问修饰符以及其他修饰符(如staticnewabstract等)。

2.事件的注册与移除

事件注册的目的是告诉事件的发出者,需要通知的对象。例如,按钮单击后,某个图片要放大。事件注册的实质,就是向委托的调用列表中添加方法。
注册事件要使用 += 运算符:

	事件名 += 委托实例;
	事件名 += new 委托类名(方法名);

移除一个事件注册使用 -= 运算符:

	事件名 -= 委托实例;
	事件名 -= new 委托类名(方法名);

注意,在声明事件的类的外部,对于事件的操作只能用 +=-=,而不能用其他运算符,如赋值(=)、判断是否为空(==)等。

3.事件的发生

事件的发生,就是对事件相对应的委托的调用,也就是委托的调用列表中所包含的各个方法的调用。格式如下:

	事件名(参数);

事件的典型应用

C#中允许各种委托应用于事件中,但典型的应用中,委托一般是这样的格式:

delegate void 委托名 (object sender, EventArgs e);

其中,返回类型为void,委托名有两个参数,分别表示事件的发出者以及事件发生时的一些参数。为了表示具体的参数,一般要继承EventArgs,加上更多的属性和方法。
如此说来,自定义事件,要有六步曲:

  • ① 定义具体的事件参数类型,可以从EventArgs继承;
  • ② 定义一个委托类型,如果不想自定义委托,则可以使用系统中定义的泛型委托EventHandler <TEventArgs>
  • ③ 在事件源类中定义一个事件,使用event关键字和委托类型;
  • ④ 在事件源类中的合适地方(即事件发生的时候),生成事件参数,并调用事件。
  • ⑤ 在事件的订阅者中,写一个事件方法来表示事件发生时在执行的任务;如果不写方法名,也可以使用Lambda表达式或匿名方法;
  • ⑥ 在事件订阅者中,使用 += 来注册该事件方法。

示例DownloadWithEvent.cs中,爬虫程序:

using System;

namespace ConsoleApp6事件_爬虫示例_
{
	public delegate void DownloadStartHandler(object sender, DownloadStartEventArgs e);    // 1.声明委托, 公用的
	public delegate void DownloadEndHandler(object sender, DownloadEndEventArgs e);
	public delegate void DownloadingHander(object sender, DownloadingEventArgs e);

	public class DownloadStartEventArgs
	{
		public string Url { get => _url; set => _url = value; }
		private string _url;
		public DownloadStartEventArgs(string url) { this._url = url; }
	}
	public class DownloadEndEventArgs
	{
		public string Url { get => _url; set => _url = value; }
		private string _url;
		public long ByteCount { get => _byteCount; set => _byteCount = value; }
		private long _byteCount;
		public DownloadEndEventArgs(string url, long size) { this._url = url; this._byteCount = size; }
	}
	public class DownloadingEventArgs
	{
		public string Url { get => _url; set => _url = value; }
		private string _url;
		public double Percent { get => _percent; set => _percent = value; }
		private double _percent;
		public DownloadingEventArgs(string url, double percent) { this._url = url; this._percent = percent; }
	}

	public class Crawler
	{
		public event DownloadStartHandler DownloadStart;    // 2.声明事件, 在一个类中
		public event DownloadEndHandler DownloadEnd;
		public event DownloadingHander Downloading;

		public string Name { get => name; set => name = value; }
		private string name;
		private string site;

		public Crawler(string name, string site) {
			this.name = name;
			this.site = site;
		}

		public void Craw() {
			while (true) {
				string url = GetNextUrl();
				if (url == null) break;
				long size = GetSizeOfUrl(url);

				//下载开始的事件发生
				if (DownloadStart != null) {
					DownloadStart(this, new DownloadStartEventArgs(url));
					//Console.WriteLine("不为空------------------------");
				} else
					break;

				for (long i = 0; i < size + 1024; i += 1024) {
					// 下载数据...
					System.Threading.Thread.Sleep(100);
					double percent = (int)(i * 100.0 / size);
					if (percent > 100) percent = 100;
					//下载数据的事件发生
					if (Downloading != null) {
						Downloading(this, new DownloadingEventArgs(url, percent));
					}
				}

				//下载结束的事件发生
				if (DownloadEnd != null) {
					DownloadEnd(this, new DownloadEndEventArgs(url, size));
				}
			}
		}

		private string GetNextUrl() {
			int a = rnd.Next(10);
			if (a == 0) return null;
			return site + "/Page" + a + ".htm";
		}
		private long GetSizeOfUrl(string url) {
			return rnd.Next(3000 * url.Length);
		}
		private Random rnd = new Random();

	}

	class Program
	{
		static void Main(string[] args) {
			Console.WriteLine("Hello World!");

			Crawler crawler = new Crawler("Crawer101", "http://www.gxust.edu.cn/");

			// csharp1.0要写委托
			crawler.DownloadStart += new DownloadStartHandler(ShowStart);     // 3.注册事件, 在别的类中
			crawler.DownloadEnd += new DownloadEndHandler(ShowEnd);
			crawler.Downloading += new DownloadingHander(ShowPercent);

			crawler.Craw();
		}

		private static void ShowStart(object sender, DownloadStartEventArgs e) {
			Console.WriteLine((sender as Crawler).Name + "开始下载" + e.Url);
		}
		private static void ShowEnd(object sender, DownloadEndEventArgs e) {
			Console.WriteLine("\n\r下载" + e.Url + "结束, 其下载" + e.ByteCount + "字节");
		}
		private static void ShowPercent(object sender, DownloadingEventArgs e) {
			Console.WriteLine("\r下载" + e.Url + "......." + e.Percent + "%");
		}
	}
}

事件的语法细节

C#的事件是在委托的基础上进行处理的,可以将它看成是具有委托类型的一个变量或属性,事实上,事件不是一个简单变量,而是进行了一个重要的扩展,及在事件声明中还可以声明事件的存取器,格式如下:

	修饰符 event 委托类型名 事件名
	{
		add{ ... }
		remove{ ... }
	}

事件的存取,也就是事件中委托的加入与移除,所对应的存取器就是addremove。在addremove存取器的{}中,可以写上要完成的任务。当对事件使用 +=-= 运算符时,实际上就是调用事件存取器的addremove方法。
事实上,如果在声明事件时,如果没有声明事件存取器,编译器会自动产生一个,其形式如下:

	event D e 
	{
		add{ e += value; }
		remove{ e -= value; }
	}

其中,D为委托类型名,value变量的含义与属性中的value变量相似,表示参数。声明addremove方法,使得事件的处理可以更具个性化。但要注意,对于abstract事件,不能声明事件存取器。
特别注意的是,如果声明了事件存取器,对于事件的运算符就只能是 +=-=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值