面向对象三个基本特征

封装

封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为“基类”、“父类”或“超类”。

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式有三类:实现继承、接口继承和可视继承。

Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;

Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。

OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

概念讲解

泛化(Generalization)

图表 1 泛化

在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这种关系被称为泛化(Generalization)。Person(人)是基类,Teacher(教师)、Student(学生)、Guest(来宾)是子类。

若在逻辑上B是A的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性。

例如,教师是人,Teacher 是Person的“一种”(a kind of )。那么类Teacher可以从类Person派生(继承)。

如果A是基类,B是A的派生类,那么B将继承A的数据和函数。

如果类A和类B毫不相关,不可以为了使B的功能更多些而让B继承A的功能和属性。

若在逻辑上B是A的“一种”(a kind of ),则允许B继承A的功能和属性。

聚合(组合)

图表 2 组合

若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。

例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生(继承)而成。

聚合的类型分为无、共享(聚合)、复合(组合)三类。

聚合(aggregation)

图表 3 共享

上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不需要对被聚合的类A负责。

组合(composition)

图表 4 复合

这幅图与上面的唯一区别是菱形为实心的,它代表了一种更为坚固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。

依赖

(Dependency)

图表 5 依赖

这里B与A的关系只是一种依赖(Dependency)关系,这种关系表明,如果类A被修改,那么类B会受到影响

反射技术与设计模式

反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。

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命名空间中的类通过序列化来间接地使用反射。

2、反射技术示例

下面是反射技术的示例,我们可以在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。

1Namespace ReflectionExample

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

eclaredOnly);

30 MethodInfo msayhello = ht.GetMethod("SayHello");

31 msayhello.Invoke(obj,null);

32 msayhello.Invoke(objName,null);

33 System.Console.ReadLine();

34 }

35 }

36}

1using System;

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 } BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.D

26 else

27 {

28 System.Console.WriteLine("Hello," + myName);

29 }

30 }

31 }

32}

33

3、在设计模式实现中使用反射技术

采用反射技术可以简化工厂的实现。

(1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。

(2)抽象工厂:使用反射可以减少抽象工厂的子类。

采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。

采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。

(1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。

(2)享元模式:采用反射技术实例化享元可以简化享元工厂。

委托技术与设计模式

委托技术是.NET引入的一种重要技术,使用委托可以实现对象行为的动态绑定,从而提高设计的灵活性。

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

d( );

}

}

显示一个重复调用 myDelegate的MultiCall 方法,这个方法不知道,也不必知道myDelegate的目标方法的类型、该方法具有的可访问性或者是否为静态。对它来说最重要的是目标方法与myDelegate兼容。

2、示例

下面的例子说明了委托的实现,代码如下:

1using System;

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}

委托技术与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和委托技术则是具体实现。

委托技术与设计模式实现

采用委托技术可以进一步实现用组合代替继承的思路,很多采用继承实现的关系可以采用委托实现。采用委托可以简化下列设计模式的使用。

(1)模板方法:这种方法采用继承实现具体方法,采用委托可以动态实现方法的组合。

(2)观察者:可以使用事件委托实现观察者与主题之间的通信。

(3)中介者:使用委托可以去除工件与中介者之间的耦合关系。

封装

封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为“基类”、“父类”或“超类”。

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式有三类:实现继承、接口继承和可视继承。

Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;

Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。

OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

概念讲解

泛化(Generalization)

图表 1 泛化

在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这种关系被称为泛化(Generalization)。Person(人)是基类,Teacher(教师)、Student(学生)、Guest(来宾)是子类。

若在逻辑上B是A的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性。

例如,教师是人,Teacher 是Person的“一种”(a kind of )。那么类Teacher可以从类Person派生(继承)。

如果A是基类,B是A的派生类,那么B将继承A的数据和函数。

如果类A和类B毫不相关,不可以为了使B的功能更多些而让B继承A的功能和属性。

若在逻辑上B是A的“一种”(a kind of ),则允许B继承A的功能和属性。

聚合(组合)

图表 2 组合

若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。

例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生(继承)而成。

聚合的类型分为无、共享(聚合)、复合(组合)三类。

聚合(aggregation)

图表 3 共享

上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不需要对被聚合的类A负责。

组合(composition)

图表 4 复合

这幅图与上面的唯一区别是菱形为实心的,它代表了一种更为坚固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。

依赖

(Dependency)

图表 5 依赖

这里B与A的关系只是一种依赖(Dependency)关系,这种关系表明,如果类A被修改,那么类B会受到影响

反射技术与设计模式

反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件,以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。

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命名空间中的类通过序列化来间接地使用反射。

2、反射技术示例

下面是反射技术的示例,我们可以在程序去得时动态实例化对象,获得对象的属性,并调用对象的方法。

1Namespace ReflectionExample

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

eclaredOnly);

30 MethodInfo msayhello = ht.GetMethod("SayHello");

31 msayhello.Invoke(obj,null);

32 msayhello.Invoke(objName,null);

33 System.Console.ReadLine();

34 }

35 }

36}

1using System;

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 } BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.D

26 else

27 {

28 System.Console.WriteLine("Hello," + myName);

29 }

30 }

31 }

32}

33

3、在设计模式实现中使用反射技术

采用反射技术可以简化工厂的实现。

(1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。

(2)抽象工厂:使用反射可以减少抽象工厂的子类。

采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。

采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。

(1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。

(2)享元模式:采用反射技术实例化享元可以简化享元工厂。

委托技术与设计模式

委托技术是.NET引入的一种重要技术,使用委托可以实现对象行为的动态绑定,从而提高设计的灵活性。

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

d( );

}

}

显示一个重复调用 myDelegate的MultiCall 方法,这个方法不知道,也不必知道myDelegate的目标方法的类型、该方法具有的可访问性或者是否为静态。对它来说最重要的是目标方法与myDelegate兼容。

2、示例

下面的例子说明了委托的实现,代码如下:

1using System;

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}

委托技术与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和委托技术则是具体实现。

委托技术与设计模式实现

采用委托技术可以进一步实现用组合代替继承的思路,很多采用继承实现的关系可以采用委托实现。采用委托可以简化下列设计模式的使用。

(1)模板方法:这种方法采用继承实现具体方法,采用委托可以动态实现方法的组合。

(2)观察者:可以使用事件委托实现观察者与主题之间的通信。

(3)中介者:使用委托可以去除工件与中介者之间的耦合关系。


相关内容

  • 面向对象数据模型
  • 第三节 面向对象数据模型 1.传统数据模型存在的主要问题 已于前述,目前非空间数据最主要的数据模型是层次模型.网状模型和关系模型.这里,我们分别介绍它们用于GIS 地理数据库的局限性 (1)层次模型用于GIS 地理数据库的局限性 层次模型反映了地理世界中实体之间的层次关系,在描述地理世界中自然的层次 ...

  • 系统开发规范与文档编写试题(附答案)
  • 系统开发规范与文档编写期末综合练习 一.单项选择题 1.按照软件的工作方式进行分类,能够对实时发生的事件和数据及时进行处理的软件应分类为( ). A.并行处理软件 B.分时软件 C.交互式软件 D.实时处理软件 2.非常适合于在软件开发初期很难确定用户需求的情况所采用的软件开发过程模型是( ). A ...

  • 软件工程基础知识
  • 高项笔记2: 第三四章笔记 构建模型(了解) 构件是指语义完整.语法正确和有可重用价值的单位软件,是软件重用过程中可以明确辨识的系统.结构上,它是语义描述.通讯接口和实现代码的复合体.构件是具有一定的功能,能够独立工作或能同其他构件装配起来协调工作的程序体. 构建标准的三大流派(了解) EJB(En ...

  • 软件需求工程选择题
  • 选择题 1. 软件生命周期包括哪些阶段?A A. 需求.设计.编码.单元测试.接收测试和维护阶段. B. 设计.编码.单元测试.接收测试和维护阶段. C. 需求.设计.编码.单元测试和接收测试阶段. D. 需求.设计和编码阶段. 2. 好的软件需求具有哪些特性?A A. 一致性和全面性. B. 易读 ...

  • 地理信息系统考试
  • 一. 名词解释: 1. 地理信息系统(gis ):由计算机硬件.软件和不同方法组成的系统,该系统设计用来支 持空间数据采集.管理.处理.分析.建模和显示,以便解决复杂的规划和管理问题. 2. 元数据:是关于"数据的数据",是指在空间数据库中用于描述空间数据的内容.质量. 表示方式 ...

  • 资源遥感与信息技术 复习资料(3S)
  • 不同颜色(灰度)代表不同的地表物体. ① 由于大气散射,降低了各物的对比值,用辐射增强方式减少这种影响. ② 由于地形起伏,成像化的倾斜导致RS 图像的几何变形. ☆元数据:描述空间数据的数据,是对数据的说明和描述,尽可能的反映数据的特征规律. ☆拓扑关系:在地理信息系统中,为了真实的反映物体,不仅 ...

  • 软件工程课后习题答案
  • 习 题 答 案 习题一答案 一.选择题 1. 软件的主要特性是(A B C). A) 无形 B) 高成本 C) 包括程序和文档 D) 可独立构成计算机系统 2. 软件工程三要素是(C D). A) 技术.方法和工具 B) 方法.工具和过程 C) 方法.对象和类 D) 过程.模型.方法 3. 包含风险 ...

  • 5.2 面向对象程序设计的基本概念
  • 一. 教学目标 1. 知识与技能 (1)认识面向对象程序设计中对象.类的概念及其特征. (2)认识面向对象程序设计中类.对象和实例的关系. 2. 过程与方法 (1)能够举例说明现实世界与程序世界的对象及其特征. (2)能够举例说明现实世界与程序世界的类及其特征.. (3)能够画图表示类.对象和实例三 ...

  • 机械产品方案的现代设计方法
  • 机械产品方案的现代设计方法 单位:湖北襄阳市航宇救生装备有限公司被动救生部 姓名:刘进 邮编:441003 摘要:根据目前国内外设计学者进行机械产品设计时的主要思维特点,将产品方案的设计方法概括为系统化.结构模块化.基于产品特征知识和智能四种类型. 关键词:机械产品.方案设计方法 1.系统化设计方法 ...