Skip to content

.net中委托和事件的学习

写在前

委托对于我这种比较专注于前端开发的人来说,真的是不好理解。最近看了一个东西,里面用了这个,有点傻眼了,我努力去弄懂这个东西,但是真不知道怎么去懂。很显然这个东西很有用,很多东西都是基于委托来实现的,本着究底的学习态度到处去找比较通俗易懂的资料,好难找啊。开始只找到了msdn上关于这个的介绍---delegate

如大牛所说,这篇博文要讲述什么是委托?为什么要使用它及事件的由来?委托和事件对Observer设计模式的意义等等。

网上说委托就是可以实现把方法做为变量来传递的东西。

这个东西真不好理解,所以就不用管它是什么意思了,直接来看代码。

csharp
public void GreetPeople(string name)
{
	//do something
	EnglishGreeting(name);
}

public void EnglishGreeting(string name)
{
	Console.WriteLine("hello, "+name);
}

GreetPeople用于向某人打招呼,向其传递参数name的时候,比如:welpher,在这个方法中,将会调用别一个方法EnglishGreeting,向其传递name参数,在屏幕输出一句英文的打招呼句子“hello, welpher"。

如果以后要对这个方法进行扩展,比如全球化,要加入各种语言的打招呼的方法,比如加入中文的打招呼句子。

csharp
public void ChineseGreeting(string name)
{
	Console.WriteLine("你好, "+name);
}

加了一个方法后上面的GreetPeople也要改一下了,不然不知道什么时候该调用哪个方法了。

public enum Language
{
	English,Chinese


public void GreetPeople(string name,Language lang)
{
	//do somethig
	switch(lang)
	{
		case Language.English:
			EnglishGreeting(name);break;
		case Language.Chinese:
			ChineseGreeting(name);break;
	}
}

好了,满足了客户的要求。但是不得不说这个解决方案可扩展性是很差的,以后再添加其它语言版的打招呼时,不得不反复的修改代码。

先来看看GreetPeople的方法签名:

public void GreetPeople(string name, Language lang)

这个方法里,string 是参数类型,name是参数变量,我们传递什么样的名字进去就显示什么名字,不管这个名字是哪国语言。

回到之前,向这个函数传递一个方法那该多好,传递EnglishGreeting就代表EnglishGreeting()这个方法,传递ChineseGreeting就代表ChineseGreeting()这个方法。给这个方法添加一个参数DoGreeting,那是不是可以像给name赋值一样,在调用GreetPeople()的时候,给这个DoGreeting赋值么?

public void GreetPeople(string name, *** DoGreeting)
{
	DoGreeting(name);
}

这个位置通常放置的应该是参数的类型,应该有个可以代表方法的参数,并改写GreetPeople方法,但是这个应该是什么类型才能代表DoGreeting呢?

回头看看EnglishGreeting和ChineseGreeting,这两个方法都接受一个string类型的参数,只能接受string类型,其它类型是不行的。所以DoGreeting方法的参数类型是确定的。

本例用委托的方法来实现的话,完整例子如下:

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate
{
	//定义委托,它定义了可以代表的方法的类型
	public delegate void GreetingDelegate(string name);
	class Program
	{
		private static void EnglishGreeting(string name)
		{
			Console.WriteLine("hello, "+name);
		}

		private static void ChineseGreeting(string name)
		{
			Console.WriteLine("你好, "+name);
		}

		//此方法接受一个GreetingDelegate类型的方法做为参数
		private static void GreetPeople(string name,GreetingDelegate DoGreeting)
		{
			DoGreeting(name);
		}

		static void Main(string[] args)
		{
			GreetPeople("welpher",EnglishGreeting);
			GreetPeople("锋",ChineseGreeting);
		Console.ReadKey();
		}
	}
}

函数绑定到委托

通过上面的例子,那个要传的东西就叫做委托。上面的例子中,我们可以定义几个变量:

static void Main(string[] args)
{
	string name1,name2;
	name2="welpher";
	name1="伟锋";

	GreetPeople(name2,EnglishGreeting);
	GreetPeople(name1,ChineseGreeting);
	Console.ReadKey();
}

参数string可以这样子定义,那么GreetingDelegate是不是也可以这么用呢?答案是肯定的:

static void Main(string[] args)
{
	GrettingDelegate delegate1,delegate2;
	delegate1 = EnglishGreeting;
	delegate2 = ChineseGreeting;

	GreetPeople("welpher",delegate1);
	GreetPeople("伟锋",delegate2);
	Console.ReadKey();
}

这种定法也会如预料的那样输出。委托不同于string的特性:可以多个方法赋给同一个委托,或者将多个方法绑定到一个委托,当调用这个委托的时候,将依次调用其所绑定的方法,语法如下:

static void Main(string[] args)
{
	GreetingDelegate delegate1;
	delegate1 = EnglishGreeting;
	delegate1 += ChineseGreeting;

	//将先后调用 EnglishGreeting和ChineseGreeting方法
	GreetPeople("welpher",delegate1);
	Console.ReadKey();
}

还可以绕过GreetPeople方法,直接通过委托来调用EnglishGreeting和ChineseGreeting:

static void Main(string[] args)
{
	GreetingDelegate delegate1;
	delegate1 = EnglishGreeting;
	delegate1 += ChineseGreeting;

	delegate1("welpher");
	Console.ReadKey();
}

上面这些写法,都可以概括为:声明委托,然后声明方法。下面介绍一下匿名的写法:

static void Main(string[] args)
{
	GreetingDelegate delegate1 = delegate(string name)
	{
		Console.WriteLine("你好, "+name);
	}
	delegate1("伟锋");
}

这已经比较简单了,不用声明方法,用匿名方法就行了,还有更简单的,拉姆达表达式写法:

static void Main(string[] args)
{
	GreetingDelegate delegate1 = (m => {Console.WriteLine("你好,"+m);});
	delegate1("伟锋");
}

简单的写法肯定很多地方都在用,所以知道这种写法,再也不用看到这种东西而傻眼了。

到这里,想必是对委托有了一定的了解了。未完等续。

 

参考:

1、http://www.cnblogs.com/jimmyzhang/archive/2007/09/23/903360.html