.NET反射、事件、委托技术与设计模式
来源:互联网 发布:鹿鼎记 七个老婆 知乎 编辑:程序博客网 时间:2024/05/16 09:01
反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。
1.1 .NET可执行应用程序结构
程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。
应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。
程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。反射通常具有以下用途。
(1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。
(2)使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数。
(4)使用MethodInfo了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。
(5)使用FiedInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。
(6)使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。
(7)使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。
(8)使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。
System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。
反射也可用于创建称为类型浏览器的应用程序,使用户能够选择类型,然后查看有关选定类型的信息。
此外,Jscript等语言编译器使用反射来构造符号表。System.Runtime.Serialization命名空间中的类使用反射来访问数据并确定要永久保存的字段,System.Runtime.Remoting命名空间中的类通过序列化来间接地使用反射。
1.2 反射技术示例
下面是反射技术的示例,我们可以在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。
2{
3 class Class1
4 {
5 [STAThread]
6 static void Main (string [ ] args)
7 {
8 System.Console.WriteLine(“列出程序集中的所有类型”);
9 Assembly a = Assembly.LoadFrom (“ReflectionExample.exe”);
10 Type[ ] mytypes = a.GetTypes( );
11
12 Foreach (Type t in mytypes)
13 {
14 System.Console.WriteLine ( t.Name );
15 }
16 System.Console.ReadLine ( );
17 System.Console.WriteLine (“列出HellWord中的所有方法” );
18 Type ht = typeof(HelloWorld);
19 MethodInfo[] mif = ht.GetMethods();
20 foreach(MethodInfo mf in mif)
21 {
22 System.Console.WriteLine(mf.Name);
23 }
24 System.Console.ReadLine();
25 System.Console.WriteLine("实例化HelloWorld,并调用SayHello方法");
26 Object obj = Activator.CreateInstance(ht);
27 string[] s = {"zhenlei"};
28 Object bojName = Activator.CreateInstance(ht,s);
29 BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
30 MethodInfo msayhello = ht.GetMethod("SayHello");
31 msayhello.Invoke(obj,null);
32 msayhello.Invoke(objName,null);
33 System.Console.ReadLine();
34 }
35 }
36}
2namespace ReflectionExample
3{
4 public class HelloWorld
5 {
6 string myName = null;
7 public HelloWorld(string name)
8 {
9 myName = name;
10 }
11 public HelloWorld() : this(null)
12 {}
13 public string Name
14 {
15 get
16 {
17 return myName;
18 }
19 }
20 public void SayHello()
21 {
22 if(myName == null)
23 {
24 System.Console.WriteLine("Hello World");
25 }
26 else
27 {
28 System.Console.WriteLine("Hello," + myName);
29 }
30 }
31 }
32}
33
1.3 在设计模式实现中使用反射技术
采用反射技术可以简化工厂的实现。
(1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。
(2)抽象工厂:使用反射可以减少抽象工厂的子类。
采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。
采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。
(1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。
(2)享元模式:采用反射技术实例化享元可以简化享元工厂。
2 委托技术与设计模式
委托技术是.NET引入的一种重要技术,使用委托可以实现对象行为的动态绑定,从而提高设计的灵活性。
2.1 .NET中的委托技术
.NET运行库支持称为“委托”的引用类型,其作用类似于C++中的函数指针。与函数指针不同,委托实例独立于其封装方法的类,主要是那些方法与委托类型兼容。另外,函数指针只能引用静态函数,而委托可以引用静态和实例方法。委托主要用于.NET Framework中的事件处理程序和回调函数。
所有委托都从System.Delegate继承而来并且有一个调用列表,这是在调用委托时所执行方法的一个链接列表。产生的委托可以用匹配的签名引用任何方法,没有为具有返回类型并在调用列表中包含多个方法的委托定义返回值。
可以使用的委托Cimbine及Remove方法在其调用列表中添加和移除方法。若要调用委托,可使用Invoke方法,或者使用BeginInvoke和EndInvoke方法异步调用委托。委托类的实现由运行库提供,而不由用户代码提供。
委托适用于那种在某些语言中需要用函数指针来解决的情况,但是与函数指针不同,它是面向对象和类型安全的。
委托声明定义一个类,它是从System.Delegate类派生的类。委托实例封装了一个调用列表,其中列出了一个或多个方法,每个方法称为一个可调用实体。对于实例方法,可调用实体由一个实例和该实例的方法组成;对于静态方法,可调用实体仅由一个方法组成。如果用一组合适的参数来调用一个委托实例,则该委托实例所封装的每个可调用实体都会被调用,并且使用上述同一组参数。
委托实例的一个有用的属性是它既不知道,也不关心其封装方法所属类的详细信息,对它来说最重要的是这些方法与该委托的类型兼容。即只要方法的返回类型和参数表是相同的,则方法与委托类型兼容,方法的名称不一定要与委托类相同。
定义和使用委托分为声明、实例化和调用3个步骤。委托用委托声明语法声明,如:
delegate void myDelegate( );
声明一个名为myDelegate的委托,它不带参数并且不返回任何结果,如:
class Test
{
static void F( )
{
System.Console.WriteLine (“Test.F”);
}
static void Main ( )
{
myeDelegate d = new myDelegate (F);
d ( );
}
}
创建一个myDelegate实例,然后立即调用它。这样做并没有太大的意义,因为直接调用方法会更简单。当涉及其匿名特性时,委托才能真正显示出其效果,如:
void MultiCall (myDelegate d, int count ) {
for (int I = 0; I < count; I++) {
d( );
}
}
显示一个重复调用 myDelegate的MultiCall 方法,这个方法不知道,也不必知道myDelegate的目标方法的类型、该方法具有的可访问性或者是否为静态。对它来说最重要的是目标方法与myDelegate兼容。
2.2示例
下面的例子说明了委托的实现,代码如下:
2namespace DelegateExample
3{
4 public class TemplateMethod
5 {
6 public delegate float Comp(float a,float b);
7 public Comp myComp;
8 public TemplateMethod()
9 {}
10 public float DoComp(float[] f)
11 {
12 float nf = float.NaN;
13 foreach(float df in f)
14 {
15 if(float.IsNaN(nf))
16 nf = df;
17 else
18 nf = myComp(nf,df);
19 }
20 return nf;
21 }
22
23 }
24}
2.3 委托技术与GOF设计模式中委托的关系
需要指出的是,.NET中的委托技术与GOF在《设计模式》中所提列的委托的意图一致,但在实现方法上有相当大的区别。.NET中的委托更进一步地降低了对象间的耦合性,将静态的组合关系变为运行时的动态组合关系。
GOF在《设计模式》中定义的委托是:“委托是一种组合方法,它使组合具有与继承同样的复用能力。在委托方式下,有两个对象参与处理一个请求,接受请求的对象将操作委托给它的代理者(delegate),它类似于子类将请求交给它的父类处理。使用继承时,被继承的操作总能引用接受请求的对象。在C++中通过this成员变量,在Smalltalk中则通过self。委托方式为了得到同样的效果,接受请求的对象将自身传给被委托者(代理人),使被委托的操作可以引用接受请求的对象。”
如果采用.NET的委托技术,上述结构可以更加灵活。Window不引用Rectangle即可实现Area的计算,为此首先声明一个计算面积的委托定义,示例代码如下:
public delegate float Darea();
然而在Window类中声明与这个代理一致的接口:
class Window
{
public Darea Area;
}
这里不需要引用Rectangle类,只是在执行时动态绑定即可:
Rectangle rc = new Rectangle();
Window w = new Window();
w.Area = new Darea(rc.Area);
这样当调用w的Area时,实际调用的是Reactangel的Area方法。从实现意图上看,.NET的委托更好地实现了GOF所阐述的意图,结构上也更为灵活。但这两种委托解决的不是一个层面的问题,GOF的委托强调的是一种策略,而.NET和委托技术则是具体实现。
2.4 委托技术与设计模式实现
采用委托技术可以进一步实现用组合代替继承的思路,很多采用继承实现的关系可以采用委托实现。采用委托可以简化下列设计模式的使用。
(1)模板方法:这种方法采用继承实现具体方法,采用委托可以动态实现方法的组合。
(2)观察者:可以使用事件委托实现观察者与主题之间的通信。
(3)中介者:使用委托可以去除工件与中介者之间的耦合关系。
Delegate
delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。
实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1.声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并将你想要传递的函数作为参数传入。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。using System;
public class MyDelegateTest
{
// 步骤1,声明delegate对象
public delegate void MyDelegate(string name);
// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型
public static void MyDelegateFunc(string name)
{
Console.WriteLine("Hello, ", name);
}
public static void Main()
{
// 步骤2,创建delegate对象
MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
// 步骤3,调用delegate
md("sam1111");
}
}
输出结果是:Hello, sam1111
了解了delegate,下面我们来看看,在C#中对事件是如何处理的。
C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。
就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。
下面是一个简单的例子:
using System;
public class EventTest
{
// 步骤1,定义delegate对象
public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步骤2省略
public class MyEventCls
{
// 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
// 步骤4,用event关键字定义事件对象
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
: // 步骤5,用+=操作符将事件添加到队列中
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
// 步骤6,以调用delegate的方式写事件触发函数
protected void OnMyEvent(System.EventArgs e)
{
if(myevent != null)
myevent(this, e);
}
public void RaiseEvent()
{
EventArgs e = new EventArgs();
: // 步骤7,触发事件
OnMyEvent(e);
}
public static void Main()
{
EventTest et = new EventTest();
Console.Write("Please input ''a'':");
string s = Console.ReadLine();
if(s == "a")
{
et.RaiseEvent();
}
else
{
Console.WriteLine("Error");
}
}
}
输出结果如下,红色为用户的输入:
Please input ‘a’: a
My event is ok
1.委派的实现过程。
首先来看一下委派,委派其实就是方法的传递,并不定义方法的实现。事件其实就是标准化了的委派,为了事件处理过程特制的、稍微专业化一点的组播委派(多点委派)。下面举一个例子,我觉得把委派的例子和事件的例子比较,会比较容易理解。
using System;
class Class1
{
delegate int MathOp(int i1,int i2);
static void Main(string[] args)
{
MathOp op1=new MathOp(Add);
MathOp op2=new MathOp(Multiply);
Console.WriteLine(op1(100,200));
Console.WriteLine(op2(100,200));
Console.ReadLine();
}
public static int Add(int i1,int i2)
{
return i1+i2;
}
public static int Multiply(int i1,int i2)
{
return i1*i2;
}
}
首先代码定义了一个委托MathOp,其签名匹配与两个函数Add()和Multiply()的签名(也就是其带的参数类型数量相同):
delegate int MathOp(int i1,int i2);
Main()中代码首先使用新的委托类型声明一个变量,并且初始化委托变量.注意,声明时的参数只要使用委托传递的函数的函数名,而不加括号:
MathOp op1=new MathOp(Add);
(或为MathOp op1=new MathOp(Multiply);)
委托传递的函数的函数体:
public static int Add(int i1,int i2)
{
return i1+i2;
}
public static int Multiply(int i1,int i2)
{
return i1*i2;
}
然后把委托变量看作是一个函数名,将参数传递给函数。 Console.WriteLine(op1(100,200));
Console.WriteLine(op2(100,200));
2.事件的实现过程
using System;
class Class1
{
static void Main(string[] args)
{
Student s1=new Student();
Student s2=new Student();
s1.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
s2.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
s1.Register();
s2.Register();
Console.ReadLine();
}
static void Student_RegisterOK()
{
Console.WriteLine("Hello");
}
}
class Student
{
public delegate void DelegateRegisterOkEvent();
public event DelegateRegisterOkEvent RegisterOK;
public string Name;
public void Register()
{
Console.WriteLine("Register Method");
RegisterOK();
}
}
在Student类中,先声明了委托DelegateRegisterOkEvent(),然后使用event和要使用的委托类型(前面定义的DelegateRegisterOkEvent委托类型)声明事件RegisterOK(可以看作是委托的一个实例。):
public delegate void DelegateRegisterOkEvent();
public event DelegateRegisterOkEvent RegisterOK;
然后在Main()函数中,实例化Student类,然后s1.RegisterOK事件委托给了Student_RegisterOK 方法。通过“+=”(加等于)操作符非常容易地为.Net对象中的一个事件添加一个甚至多个响应方法;还可以通过非常简单的“-=”(减等于)操作符取消这些响应方法。
然后,当调用s1.Register()时,事件s1.RegisterOK发生。
3.C#中预定义事件处理方式
学习事件,我觉得最不好理解的就是C#中预定义了事件,使我才开始学习事件时一头雾水。在查了些资料后,终于弄明白了一些,如下:
EventArgs是包含事件数据的类的基类,用于传递事件的细节。
EventHandler是一个委托声明如下(其在.Net类库中如下声明的)
public delegate void EventHandler( object sender , EventArgs e ) 所以,所有形如:
void 函娄名(object 参数名,EventArgs 参数名);
的函数都可以作为Control类的Click事件响应方法了。如下面所定义的一个事件响应方法:
private void button1_Click(object sender, System.EventArgs e)
参数object sender表示引发事件的对象,(其实这里传递的是对象的引用,如果是button1的click事件则sender就是button1)System.EventArgs e 代表事件的相应信息,如鼠标的x,y值等。
下面我们研究一下Button类看看其中的事件声明,以Click事件为例。
public event EventHandler Click;
这里定义了一个EventHandler类型的事件Click
private void button1_Click(object sender, System.EventArgs e)
{
...
}
这是我们和button1_click事件所对应的方法。注意方法的参数符合委托中的签名(既参数列表)。那我们怎么把这个方法和事件联系起来呢,请看下面的代码。
this.button1.Click += new System.EventHandler(this.button1_Click); (其实button1.Click 为System.EventHandler委派的实例事件。与委派中委派实例委托给某一方法非常相似)
把this.button1_Click方法绑定到this.button1.Click事件。
4.事件的参数的使用。
using System;
class Class1
{
static void Main()
{
Student s1=new Student();
s1.Name ="Student1";
Student s2=new Student();
s2.Name ="Student2";
s1.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
s2.RegisterOK +=new Student.DelegateRegisterOkEvent(Student_RegisterOK);
//当Register方法一执行,触发RegisterOK事件
//RegisterOK事件一触发,然后执行Student_RegisterOK方法
s1.Register();
s2.Register();
Console.ReadLine();
}
static void Student_RegisterOK(RegisterOkArgs e)
{
Console.WriteLine(e.EventInfo);
}
}
class Student
{
public delegate void DelegateRegisterOkEvent(RegisterOkArgs e);
public event DelegateRegisterOkEvent RegisterOK;
public string Name;
public void Register()
{
Console.WriteLine("Register Method");
RegisterOK(new RegisterOkArgs("Student Name: "+Name));
}
}
class RegisterOkArgs:EventArgs
{
public string EventInfo;
public RegisterOkArgs(string eventInfo):base()
{
this.EventInfo =eventInfo;
}
}
示例代码:
...{
EventHandler en = new EventHandler(Form1_EventH);
EventHandler er = new EventHandler(Form1_EventH);
this.EventH += en;
this.EventH += er;
this.EventH += er;
er += en + er;
}
多播委托与事件的多次响应有极大的相似之处如图:
EventH是我定义的事件。从监视窗口可以看到EventH中有一个_invocationList这样一个成员。它是一个object数组大家一看就会明白它其实就是用来存储响应这个事件的委托数组。
委托,监视如图:
这时大家应该很惊讶,委托内部也有一个_invoationList来存储它里面的委托。从而来实现多播委托。
清晰了解事件
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication5
...{
class Program
...{
static void Main(string[] args)
...{
Console.Write("Please input a number:");
//声明MainMethod类的一个实例
MainMethod m = new MainMethod();
//把我们的方法挂到 m 的NumberAdded事件上
//请注意这里使用了 MainMethod.EventDelegate 这样的方法
//来声明这个事件委托,因为委托也是一种类。
//在类中声明一个委托,就像声明了一个嵌套的类。
m.NumberAdded += new MainMethod.EventDelegate(OutputResult);
//转换用户输入,并调用Add方法相加
m.Add(int.Parse(Console.ReadLine()));
Console.ReadKey(false);
}
//这个是我们的事件处理函数。
static void OutputResult(object sender, EventArgs e)
...{
Console.WriteLine("Some Number were added!");
}
}
public class MainMethod
...{
//首先声明一个委托
//委托的实例还叫委托,但这里,我们声明的是一个委托类型
//请注意是“类型”。
public delegate void EventDelegate(object sender, EventArgs e);
//然后声明一个事件
//使用刚定义的委托类型
public event EventDelegate NumberAdded;
public int Result;
public void Add(int x)
...{
Result = x + 55;
//比较常用的方法,如果事件不为空,就调用。
//其实是说委托EventDelegate是否指向一个方法
//如果有,就不为空。
if (NumberAdded != null)
...{
NumberAdded(this, new EventArgs());
}
}
}
}
从这段代码可以看出,在这里事件(NumberAdded)被声明为一个类型(MainMethod)的成员,当发生了一些事(有数被相加了),对象(这个类的实例m)就调用这个委托。如果我们写好一个方法(OutputResult),然后传递给这个委托(m.NumberAdded += new MainMethod.EventDelegate(OutputResult);)那么它就会做出我们希望它做的事情(输出“Some Number were added!”消息)。
学习C#委托总结
学过C了,对委托就比较有概念性的认识,C#中的委托其实类似于C中的函数指针,C中我们可以把函数进行指针化,但在C#中不允许这么操作,所以C#中委托是安全的,它并不是直接对内存进行指向,而是编成中间件代码,所以这一点C#比较好,我们应该把委托看成一个新的概念,类似于类,只不过是个特殊的类,它需要声明,然后构造的时候要有一个与声明的参数,返回值一样的方法作为参数进行构造即可.
public delegate void processdelegate();//定义一个委托,一般不用pubic
public void chuli()//定义委托的匹配签名(事件处理)
{
Response.Write("aaaaaaaaaa");
}
private void Button2_Click(object sender, System.EventArgs e)
{
processdelegate process;
process=new processdelegate(chuli);
//Response.Write("Result:{0}",process(param1,param2));//有返回值的一般这样执行
process();//无返回值的一般这样执行
}
精彩的委托示例
有许多人问的,.Net中的委托以及事件处理。我拿简单的例子说明一下,是现实中的例子:
比如说一个公司(场景),你是老板,手下有两个员工,小张和小王。
你命令小王,如果小张玩游戏,则小王扣去小张500元钱。
这就是现实中的委托。
实际上,在写程序中,程序员就是老板,小张和小王就是两个对象。小张玩游戏是一个方法,小张还有一个游戏事件,他玩游戏激发这个事件。而小王就是事件处理对象,他负责把小张的钱扣除500。
所以,委托有如下几个要素:
1 激发事件的对象--就是小张
2 处理对象事件的对象--就是小王
3 定义委托,就是你让小王监视小张。
如果这三个要素都满足的话,则你就写出了一个完整事件的处理。
下面有个例子:在vs.net2003 C#控制台应用程序编辑运行成功:
using System;
namespace CSharpConsole
{
public class 场景
{
[STAThread]
public static void Main(string[] args)
{
Console.WriteLine("场景开始了....");
// 生成小王
小王 w = new 小王();
// 生成小账
小张 z = new 小张();
// 指定监视
z.PlayGame += new PlayGameHandler(w.扣钱);
// 开始玩游戏
z.玩游戏();
console.writeline("场景结束...");
Console.ReadLine();
}
}
// 负责扣钱的人
public class 小王
{
public 小王()
{
Console.WriteLine("生成小王...");
}
public void 扣钱(object sender,EventArgs e)
{
Console.WriteLine("小王:好小子,上班时间胆敢玩游戏...");
Console.WriteLine("小王:看看你小子有多少钱...");
小张 f = (小张)sender;
Console.WriteLine("小张的钱: " + f.钱.ToString());
Console.WriteLine("开始扣钱......");
System.Threading.Thread.Sleep(500);
f.钱 = f.钱 - 500;
Console.WriteLine("扣完了....现在小张还剩下:" + f.钱.ToString());
}
}
// 如果玩游戏,则引发事件
public class 小张
{
// 先定义一个事件,这个事件表示“小张”在玩游戏。
public event PlayGameHandler PlayGame;
// 保存小张钱的变量
private int m_Money;
public 小张()
{
Console.WriteLine("生成小张....");
m_Money = 1000; // 构造函数,初始化小张的钱。
}
public int 钱 // 此属性可以操作小张的钱。
{
get
{
return m_Money;
}
set
{
m_Money = value;
}
}
public void 玩游戏()
{
Console.WriteLine("小张开始玩游戏了.....");
Console.WriteLine("小张:CS好玩,哈哈哈! 我玩.....");
System.Threading.Thread.Sleep(500);
System.EventArgs e = new EventArgs();
OnPlayGame(e);
}
protected virtual void OnPlayGame(EventArgs e)
{
if(PlayGame != null)
{
PlayGame(this,e);
}
}
}
// 定义委托处理程序
public delegate void PlayGameHandler(object sender,System.EventArgs e);
}
C#委托,事件理解入门
下面是btnRun的click事件:
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace Events
{
/**//// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
Counter oCounter = null;
private System.Windows.Forms.Button cmdRun;
private System.Windows.Forms.TextBox txtReachable;
private System.Windows.Forms.TextBox txtCountTo;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button btnRemoveDelegate;
/**//// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
oCounter = new Counter();
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(oCounter_NumberReached2);
}
/**//// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Windows Form Designer generated code#region Windows Form Designer generated code
/**//// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.cmdRun = new System.Windows.Forms.Button();
this.txtReachable = new System.Windows.Forms.TextBox();
this.txtCountTo = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btnRemoveDelegate = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// cmdRun
//
this.cmdRun.Location = new System.Drawing.Point(16, 72);
this.cmdRun.Name = "cmdRun";
this.cmdRun.Size = new System.Drawing.Size(48, 23);
this.cmdRun.TabIndex = 2;
this.cmdRun.Text = "Run";
this.cmdRun.Click += new System.EventHandler(this.cmdRun_Click);
//
// txtReachable
//
this.txtReachable.Location = new System.Drawing.Point(144, 40);
this.txtReachable.Name = "txtReachable";
this.txtReachable.Size = new System.Drawing.Size(56, 20);
this.txtReachable.TabIndex = 1;
this.txtReachable.Text = "";
//
// txtCountTo
//
this.txtCountTo.Location = new System.Drawing.Point(144, 16);
this.txtCountTo.Name = "txtCountTo";
this.txtCountTo.Size = new System.Drawing.Size(56, 20);
this.txtCountTo.TabIndex = 0;
this.txtCountTo.Text = "";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(16, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(51, 13);
this.label1.TabIndex = 3;
this.label1.Text = "Count To";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(16, 40);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(99, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Reach this number";
//
// btnRemoveDelegate
//
this.btnRemoveDelegate.Location = new System.Drawing.Point(16, 104);
this.btnRemoveDelegate.Name = "btnRemoveDelegate";
this.btnRemoveDelegate.Size = new System.Drawing.Size(168, 23);
this.btnRemoveDelegate.TabIndex = 5;
this.btnRemoveDelegate.Text = "Remove second handler";
this.btnRemoveDelegate.Click += new System.EventHandler(this.btnRemoveDelegate_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(224, 134);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.btnRemoveDelegate,
this.label2,
this.label1,
this.txtCountTo,
this.txtReachable,
this.cmdRun});
this.Name = "Form1";
this.Text = "Events";
this.ResumeLayout(false);
}
#endregion
/**//// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void btnRun_Click(object sender, System.EventArgs e)
{
if(txtCountTo.Text == "" || txtReachable.Text=="")
return;
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
}
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
{
MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
}
private void oCounter_NumberReached2(object sender, NumberReachedEventArgs e)
{
MessageBox.Show("Reached2: " + e.ReachedNumber.ToString());
}
private void btnRemoveDelegate_Click(object sender, System.EventArgs e)
{
oCounter.NumberReached -= new NumberReachedEventHandler(oCounter_NumberReached2);
oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), Convert.ToInt32(txtReachable.Text));
}
}
}
namespace Events
{
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
/**//// <summary>
/// Summary description for Counter.
/// </summary>
public class Counter
{
public event NumberReachedEventHandler NumberReached;
public Counter()
{
//
// TODO: Add constructor logic here
//
}
public void CountTo(int countTo, int reachableNum)
{
if(countTo < reachableNum)
throw new ArgumentException("reachableNum should be less than countTo");
for(int ctr=0;ctr<=countTo;ctr++)
{
if(ctr == reachableNum)
{
NumberReachedEventArgs e = new NumberReachedEventArgs(reachableNum);
OnNumberReached(e);
return;//don't count any more
}
}
}
protected virtual void OnNumberReached(NumberReachedEventArgs e)
{
if(NumberReached!=null)
{
NumberReached(this, e);
}
}
}
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
}
public int ReachedNumber
{
get
{
return _reached;
}
}
}
}
public delegate void SubEventHandler(object sender,CryArgs cryArgs);
public class Subject
public abstract class Observer
public class Mouse : Observer
- .NET反射、事件、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- .NET反射、委托技术与设计模式
- 学习动态性能表第八篇-(2)-V$LOCKED_OBJECT
- 学习动态性能表第九篇--V$FILESTAT
- 学习动态性能表第十篇--V$SESSION_LONGOPS
- VS2005发布、生成网站时如何设置固定的dll文件名?
- 学习动态性能表第十一篇-(1)-V$LATCH
- .NET反射、事件、委托技术与设计模式
- 学习动态性能表第十一篇-(2)-V$LATCH_CHILDREN
- 服务器应用程序不可用解决方案集
- 学习动态性能表第12篇--V$DB_OBJECT_CACHE
- JSP、Java 获取当前绝对路径
- 学习动态性能表第13篇--V$OPEN_CURSOR
- 学习动态性能表第14篇--V$PARAMETER&V$SYSTEM_PARAMETER
- 代码片段:生成验证码
- 学习动态性能表第15篇--V$ROLLSTAT