加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

.NET 事件模型教程(一)

发布时间:2020-12-16 23:05:21 所属栏目:大数据 来源:网络整理
导读:目录 事件、事件处理程序概念 问题描述:一个需要较长时间才能完成的任务 高耦合的实现 事件模型的解决方案,简单易懂的 VB.NET 版本 委托(delegate)简介 C# 实现 向“.NET Framework 类库设计指南”靠拢,标准实现 事件、事件处理程序概念 在面向对象理论

目录

事件、事件处理程序概念
问题描述:一个需要较长时间才能完成的任务
高耦合的实现
事件模型的解决方案,简单易懂的 VB.NET 版本
委托(delegate)简介
C# 实现
向“.NET Framework 类库设计指南”靠拢,标准实现
事件、事件处理程序概念

在面向对象理论中,一个对象(类的实例)可以有属性(property,获取或设置对象的状态)、方法(method,对象可以做的动作)等成员外,还有事件(event)。所谓事件,是对象内部状态发生了某些变化、或者对象做某些动作时(或做之前、做之后),向外界发出的通知。打个比方就是,对象“张三”肚子疼了,然后他站在空地上大叫一声“我肚子疼了!”事件就是这个通知。

那么,相对于对象内部发出的事件通知,外部环境可能需要应对某些事件的发生,而做出相应的反应。接着上面的比方,张三大叫一声之后,救护车来了把它接到医院(或者疯人院,呵呵,开个玩笑)。外界因应事件发生而做出的反应(具体到程序上,就是针对该事件而写的那些处理代码),称为事件处理程序(event handler)。

事件处理程序必须和对象的事件挂钩后,才可能会被执行。否则,孤立的事件处理程序不会被执行。另一方面,对象发生事件时,并不一定要有相应的处理程序。就如张三大叫之后,外界环境没有做出任何反应。也就是说,对象的事件和外界对该对象的事件处理之间,并没有必然的联系,需要你去挂接。

在开始学习之前,我希望大家首先区分“事件”和“事件处理程序”这两个概念。事件是隶属于对象(类)本身的,事件处理程序是外界代码针对对象的事件做出的反应。事件,是对象(类)的设计者、开发者应该完成的;事件处理程序是外界调用方需要完成的。简单的说,事件是“内”;事件处理程序是“外”。

了解以上基本概念之后,我们开始学习具体的代码实现过程。因为涉及代码比较多,限于篇幅,我只是将代码中比较重要的部分贴在文章里,进行解析,剩余代码还是请读者自己查阅,我已经把源代码打了包提供下载。我也建议你对照这些源代码,来学习教程。[下载本教程的源代码]

[TOP]

问题描述:一个需要较长时间才能完成的任务

Demo 1A,问题描述。这是一个情景演示,也是本教程中其他 Demo 都致力于解决的一个“实际问题”:Worker 类中有一个可能需要较长时间才能完成的方法 DoLongTimeTask:

using System;
using System.Threading;

namespace percyboy.EventModelDemo.Demo1A
{
// 需要做很长时间才能完成任务的 Worker,没有加入任何汇报途径。
public class Worker
{
// 请根据你的机器配置情况,设置 MAX 的值。
// 在我这里(CPU: AMD Sempron 2400+,DDRAM 512MB)
// 当 MAX = 10000,任务耗时 20 秒。
private const int MAX = 10000;

public Worker()
{
}

public void DoLongTimeTask()
{
int i;
bool t = false;
for (i = 0; i <= MAX; i++)
{
// 此处 Thread.Sleep 的目的有两个:
// 一个是不让 CPU 时间全部耗费在这个任务上:
// 因为本例中的工作是一个纯粹消耗 CPU 计算资源的任务。
// 如果一直让它一直占用 CPU,则 CPU 时间几乎全部都耗费于此。
// 如果任务时间较短,可能影响不大;
// 但如果任务耗时也长,就可能会影响系统中其他任务的正常运行。
// 所以,Sleep 就是要让 CPU 有机会“分一下心”,
// 处理一下来自其他任务的计算请求。
//
// 当然,这里的主要目的是为了让这个任务看起来耗时更长一点。
Thread.Sleep(1);

t = !t;
}
}
}
}
界面很简单(本教程中其他 Demo 也都沿用这个界面,因为我们主要的研究对象是 Worker.cs):

单击“Start”按钮后,开始执行该方法。(具体的机器配置条件,完成此任务需要的时间也不同,你可以根据你的实际情况调整代码中的 MAX 值。)

在没有进度指示的情况下,界面长时间的无响应,往往会被用户认为是程序故障或者“死机”,而实际上,你的工作正在进行还没有结束。此次教程就是以解决此问题为实例,向你介绍 .NET 中事件模型的原理、设计与具体编码实现。

[TOP]

高耦合的实现

Demo 1B,高度耦合。有很多办法可以让 Worker 在工作的时候向用户界面报告进度,比如最容易想到的:

public void DoLongTimeTask()
{
int i;
bool t = false;
for (i = 0; i <= MAX; i++)
{
Thread.Sleep(1);

t = !t;

在此处书写刷新用户界面状态栏的代码
}
}
如果说 DoLongTimeTask 是用户界面(Windows 窗体)的一个方法,那么上面蓝色部分或许很简单,可能只不过是如下的两行代码:

double rate = (double)i / (double)MAX;
this.statusbar.Text = String.Format(@"已完成 {0:P2} ...",rate);不过这样的话,DoLongTimeTask 就是这个 Windows 窗体的一部分了,显然它不利于其他窗体调用这段代码。那么:Worker 类应该作为一个相对独立的部分存在。源代码 Demo1B 中给出了这样的一个示例(应该还有很多种、和它类似的方法):

Windows 窗体 Form1 中单击“Start”按钮后,初始化 Worker 类的一个新实例,并执行它的 DoLongTimeTask 方法。但你应该同时看到,Form1 也赋值给 Worker 的一个属性,在 Worker 执行 DoLongTimeTask 方法时,通过这个属性刷新 Form1 的状态栏。Form1 和 Worker 之间相互粘在一起:Form1 依赖于 Worker 类(因为它单击按钮后要实例化 Worker),Worker 类也依赖于 Form1(因为它在工作时,需要访问 Form1)。这二者之间形成了高度耦合。

高度耦合同样不利于代码重用,你仍然无法在另一个窗体里使用 Worker 类,代码灵活度大为降低。正确的设计原则应该是努力实现低耦合:如果 Form1 必须依赖于 Worker 类,那么 Worker 类就不应该再反过来依赖于 Form1。

下面我们考虑使用 .NET 事件模型解决上述的“高度耦合”问题:

让 Worker 类在工作时,向外界发出“进度报告”的事件通知(RateReport)。同时,为了演示更多的情景,我们让 Worker 类在开始 DoLongTimeTask 之前发出一个“我要开始干活了!总任务数有 N 件。”的事件通知(StartWork),并在完成任务时发出“任务完成”的事件通知(EndWork)。

采用事件模型后,类 Worker 本身并不实际去刷新 Form1 的状态栏,也就是说 Worker 不依赖于 Form1。在 Form1 中,单击“Start”按钮后,Worker 的一个实例开始工作,并发出一系列的事件通知。我们需要做的是为 Worker 的事件书写事件处理程序,并将它们挂接起来。

[TOP]

事件模型的解决方案,简单易懂的 VB.NET 版本

Demo 1C,VB.NET 代码。虽然本教程以 C# 为示例语言,我还是给出一段 VB.NET 的代码辅助大家的理解。因为我个人认为 VB.NET 的事件语法,能让你非常直观的领悟到 .NET 事件模型的“思维方式”:

Public Class Worker
Private Const MAX = 10000

Public Sub New()
End Sub

' 注:此例的写法不符合 .NET Framework 类库设计指南中的约定,
' 只是为了让你快速理解事件模型而简化的。
' 请继续阅读,使用 Demo 1F 的 VB.NET 标准写法。
'

' 工作开始事件,并同时通知外界需要完成的数量。
Public Event StartWork(ByVal totalUnits As Integer)

' 进度汇报事件,通知外界任务完成的进度情况。
Public Event RateReport(ByVal rate As Double)

' 工作结束事件。
Public Event EndWork()

Public Sub DoLongTimeTask()
Dim i As Integer
Dim t As Boolean = False
Dim rate As Double

' 开始工作前,向外界发出事件通知
RaiseEvent StartWork(MAX)

For i = 0 To MAX
Thread.Sleep(1)
t = Not t

rate = i / MAX
RaiseEvent RateReport(rate)
Next

RaiseEvent EndWork()

End Sub
首先是事件的声明部分:你只需写上 Public Event 关键字,然后写事件的名称,后面的参数部分写上需要发送到外界的参数声明。

然后请注意已标记为蓝色的 RaiseEvent 关键字,VB.NET 使用此关键字在类内部引发事件,也就是向外界发送事件通知。请注意它的语法,RaiseEvent 后接上你要引发的事件名称,然后是具体的事件参数值。

从这个例子中,我们可以加深对事件模型的认识:事件是对象(类)的成员,在对象(类)内部状态发生了一些变化(比如此例中 rate 在变化),或者对象做一些动作时(比如此例中,方法开始时,向外界 raise event;方法结束时,向外界 raise event),对象(类)发出的通知。并且,你也了解了事件参数的用法:事件参数是事件通知的相关内容,比如 RateReport 事件通知需要报告进度值 rate,StartWork 事件通知需要报告总任务数 MAX。

我想 RaiseEvent 很形象的说明了这些道理。

[TOP]

委托(delegate)简介。

在学习 C# 实现之前,我们首先应该了解一些关于“委托”的基础概念。

你可以简单的把“委托(delegate)”理解为 .NET 对函数的包装(这是委托的主要用途)。委托代表一“类”函数,它们都符合一定的规格,如:拥有相同的参数个数、参数类型、返回值类型等。也可以认为委托是对函数的抽象,是函数的“类”(类是具有某些相同特征的事物的抽象)。这时,委托的实例将代表一个具体的函数。

你可以用如下的方式声明委托:

public delegate void MyDelegate(int integerParameter);如上的委托将可以用于代表:有且只有一个整数型参数、且不带返回值的一组函数。它的写法和一个函数的写法类似,只是多了 delegate 关键字、而没有函数体。(注:本文中的函数(function),取了面向过程理论中惯用的术语。在完全面向对象的 .NET/C# 中,我用以指代类的实例方法或静态方法(method),希望不会因此引起误解。顺带地,既然完全面向对象,其实委托本身也是一种对象。)

委托的实例化:既然委托是函数的“类”,那么使用委托之前也需要实例化。我们先看如下的代码:

public class Sample
{
public void DoSomething(int mode)
{
Console.WriteLine("test function.");
}

public static void Hello(int world)
{
Console.WriteLine("hello,world!");
}
}
我们看到 Sample 的实例方法 DoSomething 和静态方法 Hello 都符合上面已经定义了的 MyDelegate 委托的“规格”。那么我们可以使用 MyDelegate 委托来包装它们,以用于特殊的用途(比如下面要讲的事件模型,或者将来教程中要讲的多线程模型)。当然,包装的过程其实也是委托的实例化过程:

Sample sp = new Sample();
MyDelegate del = new MyDelegate(sp.DoSomething);
这是对上面的实例方法的包装。但如果这段代码写在 Sample 类内部,则应使用 this.DoSomething 而不用新建一个 Sample 实例。对 Sample 的 Hello 静态方法可以包装如下:

MyDelegate del = new MyDelegate(Sample.Hello);调用委托:对于某个委托的实例(其实是一个具体的函数),如果想执行它:

del(12345);直接写上委托实例的名字,并在括号中给相应的参数赋值即可。(如果函数有返回值,也可以像普通函数那样接收返回值)。

[TOP]

C# 实现

Demo 1D,C# 实现。这里给出 Demo 1C 中 VB.NET 代码的 C# 实现:是不是比 VB.NET 的代码复杂了一些呢?

using System;
using System.Threading;

namespace percyboy.EventModelDemo.Demo1D
{
// 需要做很长时间才能完成任务的 Worker,这次我们使用事件向外界通知进度。
public class Worker
{
private const int MAX = 10000;

// 注:此例的写法不符合 .NET Framework 类库设计指南中的约定,
// 只是为了让你快速理解事件模型而简化的。
// 请继续阅读,使用 Demo 1E / Demo 1H 的 C# 标准写法。
//

public delegate void StartWorkEventHandler(int totalUnits);
public delegate void EndWorkEventHandler();
public delegate void RateReportEventHandler(double rate);

public event StartWorkEventHandler StartWork;
public event EndWorkEventHandler EndWork;
public event RateReportEventHandler RateReport;

public Worker()
{
}

public void DoLongTimeTask()
{
int i;
bool t = false;
double rate;

if (StartWork != null)
{
StartWork(MAX);
}

for (i = 0; i <= MAX; i++)
{
Thread.Sleep(1);
t = !t;
rate = (double)i / (double)MAX;

if (RateReport != null)
{
RateReport(rate);
}
}

if (EndWork != null)
{
EndWork();
}
}
}
}
这份代码和上面 VB.NET 代码实现一致的功能。通过 C# 代码,我们可以看到被 VB.NET 隐藏了的一些实现细节:

首先,这里一开始声明了几个委托(delegate)。然后声明了三个事件,这里请注意 C# 事件声明的方法:

public event [委托类型] [事件名称];这里你可以看到 VB.NET 隐藏了声明委托的步骤。

另外提醒你注意代码中具体引发事件的部分:

if (RateReport != null)
{
RateReport(rate);
}
在调用委托之前,必须检查委托是否为 null,否则将有可能引发 NullReferenceException 意外;比较 VB.NET 的代码,VB.NET 的 RaiseEvent 语句实际上也隐藏了这一细节。

好了,到此为止,Worker 类部分通过事件模型向外界发送事件通知的功能已经有了第一个版本,修改你的 Windows 窗体,给它添加 RateReport 事件处理程序(请参看你已下载的源代码),并挂接到一起,看看现在的效果:

添加了进度指示之后的界面,极大的改善了用户体验,对用户更为友好。

[TOP]

向“.NET Framework 类库设计指南”靠拢,标准实现

Demo 1E,C# 的标准实现。上文已经反复强调了 Demo 1C,Demo 1D 代码不符合 CLS 约定。微软为 .NET 类库的设计与命名提出了一些指南,作为一种约定,.NET 开发者应当遵守这些约定。涉及事件的部分,请参看事件命名指南(对应的在线网页),事件使用指南(对应的在线网页)。

using System;
using System.Threading;

namespace percyboy.EventModelDemo.Demo1E
{
public class Worker
{
private const int MAX = 10000;

public class StartWorkEventArgs : EventArgs
{
private int totalUnits;

public int TotalUnits
{
get { return totalUnits; }
}

public StartWorkEventArgs(int totalUnits)
{
this.totalUnits = totalUnits;
}
}

public class RateReportEventArgs : EventArgs
{
private double rate;

public double Rate
{
get { return rate; }
}

public RateReportEventArgs(double rate)
{
this.rate = rate;
}
}

public delegate void StartWorkEventHandler(object sender,StartWorkEventArgs e);
public delegate void RateReportEventHandler(object sender,RateReportEventArgs e);

public event StartWorkEventHandler StartWork;
public event EventHandler EndWork;
public event RateReportEventHandler RateReport;

protected virtual void OnStartWork( StartWorkEventArgs e )
{
if (StartWork != null)
{
StartWork(this,e);
}
}

protected virtual void OnEndWork( EventArgs e )
{
if (EndWork != null)
{
EndWork(this,e);
}
}

protected virtual void OnRateReport( RateReportEventArgs e )
{
if (RateReport != null)
{
RateReport(this,e);
}
}

public Worker()
{
}

public void DoLongTimeTask()
{
int i;
bool t = false;
double rate;

OnStartWork(new StartWorkEventArgs(MAX) );

for (i = 0; i <= MAX; i++)
{
Thread.Sleep(1);
t = !t;
rate = (double)i / (double)MAX;

OnRateReport( new RateReportEventArgs(rate) );
}

OnEndWork( EventArgs.Empty );
}
}
}
按照 .NET Framework 类库设计指南中的约定:

(1)事件委托名称应以 EventHandler 为结尾;

(2)事件委托的“规格”应该是两个参数:第一个参数是 object 类型的 sender,代表发出事件通知的对象(代码中一般是 this 关键字(VB.NET 中是 Me))。第二个参数 e,应该是 EventArgs 类型或者从 EventArgs 继承而来的类型;

事件参数类型,应从 EventArgs 继承,名称应以 EventArgs 结尾。应该将所有想通过事件、传达到外界的信息,放在事件参数 e 中。

(3)一般的,只要类不是密封(C# 中的 sealed,VB.NET 中的 NotInheritable)的,或者说此类可被继承,应该为每个事件提供一个 protected 并且是可重写(C# 用 virtual,VB.NET 用 Overridable)的 OnXxxx 方法:该方法名称,应该是 On 加上事件的名称;只有一个事件参数 e;一般在该方法中进行 null 判断,并且把 this/Me 作为 sender 执行事件委托;在需要发出事件通知的地方,应调用此 OnXxxx 方法。

对于此类的子类,如果要改变发生此事件时的行为,应重写 OnXxxx 方法;并且在重写时,一般情况下应调用基类的此方法(C# 里的 base.OnXxxx,VB.NET 用 MyBase.OnXxxx)。

我建议你能继续花些时间研究一下这份代码的写法,它是 C# 的标准事件实现代码,相信你会用得着它!

在 Demo 1D 中我没有讲解如何将事件处理程序挂接到 Worker 实例的事件的代码,在这个 Demo 中,我将主要的部分列在这里:

private void button1_Click(object sender,System.EventArgs e)
{
statusBar1.Text = "开始工作 ....";
this.Cursor = Cursors.WaitCursor;

long tick = DateTime.Now.Ticks;

Worker worker = new Worker();

// 将事件处理程序与 Worker 的相应事件挂钩
// 这里我只挂钩了 RateReport 事件做示意
worker.RateReport += new Worker.RateReportEventHandler(this.worker_RateReport);

worker.DoLongTimeTask();

tick = DateTime.Now.Ticks - tick;
TimeSpan ts = new TimeSpan(tick);

this.Cursor = Cursors.Default;
statusBar1.Text = String.Format("任务完成,耗时 {0} 秒。",ts.TotalSeconds);
}

private void worker_RateReport(object sender,Worker.RateReportEventArgs e)
{
this.statusBar1.Text = String.Format("已完成 {0:P0} ....",e.Rate);
}
请注意 C# 的挂接方式(“+=”运算符)。

到这里为此,你已经看到了事件机制的好处:Worker 类的代码和这个 Windows Form 没有依赖关系。Worker 类可以单独存在,可以被重复应用到不同的地方。

VB.NET 的读者,请查看 Demo 1F 中的 VB.NET 标准事件写法,并参考这里的说明,我就不再赘述了。

[TOP]

2005年1月22日 18:27
评论
2005-1-22 22:10 | 开心就好
有没有想法到MSDN Webcast来讲一次网络讲座,把你的这些心得传播给更多的朋友呢?如果有想法的话,请将你的文章,整理成一个PPT,并且做一个十分钟左右的录音文件,发送到msdnprc^_^microsoft.com(^_^变为@)。或者发给立楠也可以。

# re: .NET 事件模型教程(一)
2005-1-24 11:43 | bestsky
受益非浅,我以前从没有这样写过谢谢.

# re: .NET 事件模型教程(一)
2005-1-25 14:59 | Truly
Good

# re: .NET 事件模型教程(一)
2005-1-27 17:10 | mouseindark
小弟愚笨,折腾了一个多小时才算是理解了
现在用.net就感觉越学越是不懂,今天算是又张了见识了

# re: .NET 事件模型教程(一)
2005-2-2 17:32 | 生如夏花
前两天面试时碰到过一道关于事件代理机制的题,今天认识的更清楚了。

# .NET 事件模型教程
2005-2-4 11:34 | morepower
Ping Back来自:blog.csdn.net

# re: .NET 事件模型教程(一)
2005-3-7 15:27 | Juno May
真的很感谢你写的这几篇文章,让我对event driven 这些很模糊的概念变得清晰起来。

我是个网站设计师,喜欢用php和function-oriented的方法写应用程序,对M$的东西都很感冒不太喜欢,但是.NET的一些概念和方法确实对开发带来便利,尽管庞大的framework 和 class 使得程序变得臃肿缓慢,但是大大加速开发进程和便于维护。

现在我们在用Prado (一个借鉴了asp.net大部分思想的php5 framework)开发应用程序,它山之石可以攻玉 :)

# re: .NET 事件模型教程(一)
2005-3-17 16:44 | 冲浪
文章很不错,有没有想过出书哦....

# re: .NET 事件模型教程(一)
2005-3-21 1:25 | Liangyj
在《.net框架程序设计》中,说到的“回调方法的原形应该有一个void返回值,并且接受两个参数,第一个参数为object类型,其指向发送通知的对象,第二个参数为一个继承自EventArgs的类型,其中包含所有通知接受者需要的附加信息”
而你在这里定义的event 并没有符合这两个规则,那么究竟是你错?还是那本书的错呢?还是有另外的解释呢?请指点一下。

# re: .NET 事件模型教程(一)
2005-3-21 9:18 | 破宝
to Liangyj:

我相信如果你读完这第一篇教程全文的话,就不会认为我写的和你那本书有矛盾。你再看看最后一个小节“向“.NET Framework 类库设计指南”靠拢,标准实现”里的内容?

# re: .NET 事件模型教程(一)
2005-4-1 4:01 | jaye
xiexie 破宝,收藏一下不介意吧

# re: .NET 事件模型教程(一)
2005-4-9 21:29 | cqhydz
我是看msdn中自定义控件哪个录像知道事件模型的,你写的不错

# re: .NET 事件模型教程(一)
2005-4-12 17:55 | Jerry
好文啊!
我事件这一张翻来复去看了几遍都没看明白。听你这么一讲解,思路清晰了很多,多谢啊!

# re: .NET 事件模型教程(一)
2005-4-21 12:33 | 凉
写的真好,之前看的msdn,一点也没看懂,现在总算有点明白了。

# re: .NET 事件模型教程(一)
2005-4-28 20:03 | dur
写得很棒。:)
偶有一问。在你的Worker.DoLongTimeTask中写了OnStartWork来让Worker对象发出一个事件。那么Form对象是怎么捕捉鼠标点了一下的呢?
如果想让某接口在收到一脉冲时产生一个事件,该怎么办呢?

# re: .NET 事件模型教程(一)
2005-5-9 3:08 | myaspx
看了此文,对事件有了更深的了解!

# re: .NET 事件模型教程(一)
2005-5-12 19:49 | EricZQWang
对那个挂钩RateReport事件没有完全理解。EndReport 和 StartReport没有挂钩,是不是就不执行了呢?
// 这里我只挂钩了 RateReport 事件做示意
worker.RateReport += new Worker.RateReportEventHandler(this.worker_RateReport);

# re: .NET 事件模型教程(一)
2005-5-12 20:02 | 破宝
to EricZQWang:
不知道你所谓“不执行”是指“谁”不执行?
正如文中一开始就说,事件是一个对象发出的消息,到了那个时候它就要发消息,无论是否有人注意这个消息。
如果一开始我们对某个事件挂接了一个handler,则在这个事件发生时,handler被执行。如果不挂钩,handler不会执行。

# re: .NET 事件模型教程(一)
2005-5-13 8:53 | EricZQWang
谢谢破宝。 我Share 一下自己新的理解:“
// 这里挂钩了 RateReport 事件做示意
worker.RateReport += new Worker.RateReportEventHandler(this.worker_RateReport); ”
他的作用就是使RateReport != null.在method:
worker.DoLongTimeTask()中,Worker总共发出了三个消息OnStartWork,OnRateReport,OnEndReport
在method: button1_Click()中挂钩了OnRateReport消息,我觉得作用就是实例化了Worker.RateReport,使RateReport != null,OnStartReport和OnEndReport没有挂钩,StartWork and EndStart ==null.
当Worker发出OnRateReport的消息时,会执行
this.statusBar1.Text = String.Format("已完成 {0:P0} ....",e.Rate);
但是:当Worker发出OnStartWork和OnEndReport消息时,
因为StartWork和EndReport==null,所以在Worker的Method:OnStartReport和OnEndReport中什么都没有作

满分100的话,这个理解可以打多少分?:)

# re: .NET 事件模型教程(一)
2005-5-13 10:55 | 破宝
应该说从一个面向过程的观点来看,你的理解基本上没什么问题(当然指出一点:“消息”是指StartWork,RateRepport,EndWork,而不是OnXxxx,后者只是worker的protected方法)。
但希望你能够上升到面向对象的角度再来领悟这个问题。祝你好运!

# re: .NET 事件模型教程(一)
2005-5-13 22:57 | gaofan
代码下不了啊,能不能给我发一份
gaofan628@yahoo.com.cn

谢谢先

# re: .NET 事件模型教程(一)
2005-5-28 20:26 | lhq
虽然我还是个新手.
不过还是觉得应该更正一下sender和e不是类型而是对象
否则你怎么进行转换呢?
类本事是没有转换这个概念的
有了继承才有了转换这个概念
而转换本身又不是针对类的
因为C#是一门面向对象的设计语言

# re: .NET 事件模型教程(一)
2005-5-29 2:13 | 破宝
to lhq:
不知道是哪句话中的措辞不严密,敬请指出。
按照.net编码指南,事件处理程序的两个参数:sender参数的类型应为object类型,e参数的类型应为EventArgs类型或者EventArgs类型的子类。

# re: .NET 事件模型教程(一)
2005-5-31 11:32 | lhq
(2)事件委托的“规格”应该是两个参数:第一个参数是 object 类型的 sender,代表发出事件通知的对象(代码中一般是 this 关键字(VB.NET 中是 Me))。第二个参数 e,应该是 EventArgs 类型或者从 EventArgs 继承而来的类型;
在最后几句里,第一句我没仔细看现在再看这句没有问题
不过第二个的e还有点问题 我认为e应该是EventArgs 类型或者从 EventArgs 继承而来的类型的对象
好比
class A{}
A a;
那A是类型,a就是对象
e就是System.EventArgs以及它所派生的类的对象
以上属个人意见,如有不当请指出

# re: .NET 事件模型教程(一)
2005-5-31 17:24 | 破宝
to lhq:
多谢指正,纯属文法上的考虑不周。

# re: .NET 事件模型教程(一)
2005-6-2 11:52 | Jerry
弱弱的问题:

在接收到事件后,我用Label来显示进度总是没办法显示,只有在事件完成后显示出一个100%,而没办法在运行事件过程中显示现在运行到百分之多少了,用了StatusBar没这个问题。

StatusBar的Text和Label的Text属性不一样吗?

# re: .NET 事件模型教程(一)
2005-6-2 13:53 | Jerry
另外,我用progressbar也不能正确显示当前进度。

好象正在运行的任务也会使窗体陷入无法响应的状态。

有没有办法使进程在运行,而窗体是有响应的:可以拖拽,可以最小话、最大化?

# re: .NET 事件模型教程(一)
2005-7-21 18:32 | lovaling
看完了,总的来说感觉C#的看起来最顺眼,个人观点,呵呵,

向“.NET Framework 类库设计指南”靠拢,标准实现 这一节我不是十分认同这样的做法(虽然是设计指南),本来一个简单的事情被搞得复杂了。第一个sender参数是必要的,使用过委托的都会有这样的需要,完全解除了设计上的藕合,结果丢失了一些参数,不得不以这样的方式来补偿。

没有必要把一个事件的参数包装到一个类里面,也许这样看起来所有的事件都有一样的外观,但我认为不如直接传递参数来得方便。如下:

namespace percyboy.EventModelDemo.Demo1E
{
public class Worker
{
private const int MAX = 10000;

public delegate void StartWorkEventHandler(object sender,int totalUnits);
public delegate void RateReportEventHandler(object sender,double rate);
public delegate void EndWorkEventHandler(object sender);

public event StartWorkEventHandler StartWork;
public event EndWorkEventHandler EndWork;
public event RateReportEventHandler RateReport;

public Worker()
{
}

public void DoLongTimeTask()
{
int i;
bool t = false;
double rate;

StartWork(this,MAX);

for (i = 0; i <= MAX; i++)
{
Thread.Sleep(1);
t = !t;
rate = (double)i / (double)MAX;

RateReport(this,rate);
}

EndWork(this);
}
}
}

sender可以保留。不过派生一个类我觉得是一种过时的老的作法,既然有简单的,为何还要用那么复杂的呢,.net里面这样也保证不会有问题。

可能是想通过一个类型来增强静态编译时的检错吧,防止有相同参数类型的事件接口绑定错了,猜想。

# re: .NET 事件模型教程(一)
2005-7-21 20:58 | 鐮村疂
to lovaling:

我的观点是“入乡随俗”。

很多从 Java 转 C# 的人在书写代码时,把类、方法、属性等等的命名,按照 Java 的命名规则去做,这样做虽然没什么坏处,但看起来总是感觉不伦不类的。所以我的观点是“入乡随俗”,做 .NET 就按微软给大家的约定,没什么不好;做 Java 就看 Sun 的编码规范。这样做出来的东西,感觉才是那个“味道”。

另外,关于事件参数都从 EventArgs 继承这一点,我认为是有好处的,不过好处不是定义事件的一方,而是调用事件的一方。

比如,我们在 VS.NET 中写代码时,如果是某事件handler的代码,你可以直接写 e ,然后一“点”就把所有跟此事件相关的参数成员点出来了,使用起来还是很方便。

我们在使用微软提供的标准类库时,形成了这样的习惯,那么在定义自己的事件时,没必要一定自创一套。标准类库的事件遵循一套标准,你自定义的遵循另一套标准,这样感觉还是会带来一定程度的混乱和迷惑。

# re: .NET 事件模型教程(一)
2005-9-22 15:32 | public
在 委托(delegate)简介 一节中 “顺带地,既然完全面向对象,其实委托本身也是一种对象。” 一句话题出疑义,我好像记得: 委托(delegate)是一种类(class)。

# re: .NET 事件模型教程(一)
2005-11-5 17:07 | sunw
感谢楼上的,终于弄懂了C#里面的事件代理.

# re: .NET 事件模型教程(一)
2006-2-13 14:26 | yao
好文章.以前不清楚的概念现在清楚了.

# re: .NET 事件模型教程(一)
2006-3-7 19:22 | wqxh
确实不错,以前比较模糊的概念现在越来越清楚了.
希望破宝写出更加好的文章.

# re: .NET 事件模型教程(一)
2006-7-2 12:59 | 野风
不错,很喜欢你的文章,有独道的见解...

# .NET 事件模型教程(一)
2006-8-29 11:52 | after_
.NET 事件模型教程(一)
目录

事件、事件处理程序概念
问题描述:一个需要较长时间才能完成的任务
高耦合的实现
事件模型的解决方案,简单易懂的 VB.NET 版本
委托(delegate)简介
C# 实现
向“.NET Framework 类库设计指南”靠拢,标准实现
事件、事件处理程序概念


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jelink/archive/2006/08/30/1144459.aspx

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读