您的位置:首页 > 其它

委托, 匿名委托, Lambda表达式, 事件的本质, 以及Observer模式.

2011-08-31 17:03 621 查看
1.委托的本质

委托实现了面向对象的,类型安全的方法回调机制。委托看上去就一句话, 很简单, 但在本质上它是一个类,CLR和编译器在后台会将委托自动编译为一个类.
该类继承自System.MulticastDelegate类,该类维护一个委托列表,在调用多播委托时,将按照委托列表的委托顺序而调用的。该类包括一个接受两个参数的构造函数和3个重要方法:BeginInvoke、EndInvoke和Invoke。

下面是delegate委托的父类MulticastDelegate的部分代码:
public abstract class MulticastDelegate : Delegate
{
public sealed override Delegate[] GetInvocationList();
// Overloaded operators.
public static bool operator ==(MulticastDelegate d1, MulticastDelegate d2);
public static bool operator !=(MulticastDelegate d1, MulticastDelegate d2);
// Used internally to manage the list of methods maintained by the delegate.
private IntPtr _invocationCount;
private object _invocationList;
}
由此可以看出, 这个委托列表实际上就是一个Detegate类型的数组.

另外, 委托声明放置的位置还要注意, 因为C#中允许出现嵌套类,而委托本质上就是一个类, 所以下面namespace1, namespace2两个方案都正确, 但namespace2的委托就成为了Class A的嵌套类.

namespace namespace1

{
public delegate void MyEventDelegate(int value);
public class A
{

}

}

namespace namespace2

{
public class A
{
public delegate void MyEventDelegate(int value);
}

}

public class A
{
public class 委托
{
}
}

关于嵌套类的使用场合, 看到有帖子说就是如果A类中需要有某些代码需要以面向对象的形式展现,而又不想被除调用类以外的类调用到就可以使用嵌套类,需要注意的是嵌套类最好不要使用public访问符,因为这样无任何意义。
如此一来, 建议将委托放置在其他类外面声明.

一个完整的委托例子:

namespace LatestWebTestSite
{

public delegate int DelegateAbc(int a, int b);
public partial class _Default : System.Web.UI.Page
{
public int MethodAdd (int a, int b)
{
return a + b;
}
protected void Page_Load(object sender, EventArgs e)
{
DelegateAbc abc = MethodAdd;
Response.Write(abc(100, 200));
}
}

}

2.匿名委托(方法)

其实就是将方法定义与委托变量赋值两步并作一步了, 省了为方法起名了, 写起来省事而已.

但编译后就知道了, 实际上编译器又将一步分作两步, 并给该方法起了个随机的名字, 匿名方法也就是个语法糖而已.

button1.Click += delegate
{
MessageBox.Show("Hello world.");
};

3.Lambda表达式(=>)

第一眼看到觉得是个很古怪的一个名字,有点排斥它。

MSDN Lambda 表达式(http://msdn.microsoft.com/zh-cn/library/bb397687.aspx)上的定义: “Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。

所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。 该 Lambda 运算符的左边是输入参数(如果有),右边包含表达式或语句块。 Lambda 表达式 x => x * x 读作“x goes to x times x”。可以将此表达式分配给委托类型.

它是对匿名方法的进一步简化, 也是语法糖, 编译器会搞定将其一步步分开, 并起名的. Lambda表达式为LINQ提供了语法基础。

来个例子如下:


class Program
{
delegate void delgateLzd(int iValue);

static void Main(string[] args)
{
delgateLzd del = x => {

Console.WriteLine(x * x);

};

del(5);

Console.ReadKey();

}
}



比较一下即可知道, 这里面用的是Lambda语句,看上去酷似匿名方法,实际上就是用=>这个符号取代了匿名方法里的delegate这个单词,可以说是对匿名方法的又一次简化。

原来想用委托要4步:1.定义委托,2.写一个符合委托的方法,3.建立委托实例并邦定刚才的方法, 4.调用委托。

到了匿名方法这个阶段就把2,3两步给合并了,不用写独立的方法了,大大方便了委托的使用。

而到了Lambda这里将匿名方法更进一步简化,连delegate都懒得写了,直接给个=>符号了事,如果需要参数就只把参数名列出来再接上=>,真是步步简化啊,到了这一步,如果是一个c#1的程序员穿越过来,看到这段代码,一定会大吃一惊,这里除了开始的委托类型以外,已经几乎看不到委托的影子了,但实际上,背地里,编译器还是给编译成了委托,这个语法糖是越来越甜,封装的越来越抽象了。

4.事件的本质

事件建立在委托之上,只有了解了委托才能明白事件的原理。事件是对委托的封装,从委托的示例中可知,在客户端可以随意对委托进行操作,一定程度上破坏了面向的对象的封装机制,因此事件实现了对委托的封装。
事件其实就是委托类型的变量,也就是说如果想声明一个事件的话,你必须先声明一个委托类型的变量,然后将event关键字放在声明体的前部,例如:ITPUB个人空间M1iT/z m*FSV0x
//声明事件委托
public delegate void CalculateEventHandler(object sender,CalculateEventArgs e);
//定义事件成员,提供外部绑定
public event CalculateEventHandler MyCalculate;

一个完整的事件例子:

namespace UseEventExample
{
public delegate void MyEventDelegate(int value, int value2);

class Program
{
public static void MethodAdd(int a, int b)
{
Console.WriteLine(a + b);
}

public static event MyEventDelegate abc; //定义一个事件

static void Main(string[] args)
{
abc = MethodAdd;
abc(100, 200);
Console.ReadKey();

}
}
}

比较一下委托与事件的两个例子就可以比较出来,

委托:DelegateAbc abc = MethodAdd;

事件:[b]Public static event MyEventDelegate abc;
abc = MethodAdd;
[/b]
事件实际上就是委托的变量而已,它的作用就是提高委托的封装性。

5.Observer模式

使用委托和事件实现的观察者模式, 让观察者和被观察者不用任何耦合, 下面是猫叫鼠跑的例子代码:

5.1 Cat

namespace ConsoleApplication1
{
public delegate void CatDelegate ();

public class Cat
{
public event CatDelegate catEvent;

public void CatCry()
{
Console.WriteLine(" Cat is crying");

if (catEvent!=null)
{
catEvent();
}
}

}
}

5.2 Mouse

namespace ConsoleApplication1
{
public class Mouse
{
public string MouseName { set; get; }

public Mouse(string mouseName)
{
this.MouseName = mouseName;
}

public void run()
{
Console.WriteLine(MouseName + " start to run");
}
}
}

5.3 Main

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Mouse mouse = new Mouse("mouse1");

Cat cat = new Cat();
cat.catEvent += mouse.run;
cat.CatCry();

Console.ReadKey();
}
}
}

当然, 正式的代码要抽象出observer, subject的接口, 然后让鼠, 猫分别实现之.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐