反射技术与设计模式
反射(Reflection)是NET中的重要机制通过放射可以在运行时获得NET中每一个类型(包括类结构委托接口和枚举等)的成员包括方法属性事件以及构造函数等还可以获得每个成员的名称限定符和参数等有了反射即可对每一个类型了如指掌如果获得了构造函数的信息即可直接创建对象即使这个对象的类型在编译时还不知道
NET可执行应用程序结构
程序代码在编译后生成可执行的应用我们首先要了解这种可执行应用程序的结构
应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次公共语言运行库加载器管理应用程序域这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局
程序集包含模块而模块包含类型类型又包含成员反射则提供了封装程序集模块和类型的对象我们可以使用反射动态地创建类型的实例将类型绑定到现有对象或从现有对象中获取类型然后调用类型的方法或访问其字段和属性反射通常具有以下用途
()使用Assembly定义和加载程序集加载在程序集清单中列出模块以及从此程序集中查找类型并创建该类型的实例
()使用Module了解包含模块的程序集以及模块中的类等还可以获取在模块上定义的所有全局方法或其他特定的非全局方法
()使用ConstructorInfo了解构造函数的名称参数访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等使用Type的GetConstructors或GetConstructor方法来调用特定的构造函数
()使用MethodInfo了解方法的名称返回类型参数访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等使用Type的GetMethods或GetMethod方法来调用特定的方法
()使用FiedInfo了解字段的名称访问修饰符(如public或private)和实现详细信息(如static)等并获取或设置字段值
()使用EventInfo了解事件的名称事件处理程序数据类型自定义属性声明类型和反射类型等添加或移除事件处理程序
()使用PropertyInfo了解属性的名称数据类型声明类型反射类型和只读或可写状态等获取或设置属性值
()使用ParameterInfo了解参数的名称数据类型是输入参数还是输出参数以及参数在方法签名中的位置等
SystemReflectionEmit命名空间的类提供了一种特殊形式的反射可以在运行时构造类型
反射也可用于创建称为类型浏览器的应用程序使用户能够选择类型然后查看有关选定类型的信息
此外Jscript等语言编译器使用反射来构造符号表SystemRuntimeSerialization命名空间中的类使用反射来访问数据并确定要永久保存的字段SystemRuntimeRemoting命名空间中的类通过序列化来间接地使用反射
反射技术示例
下面是反射技术的示例我们可以在程序去得时动态实例化对象获得对象的属性并调用对象的方法
Namespace ReflectionExample
{
class Class
{
[STAThread]
static void Main (string [ ] args)
{
SystemConsoleWriteLine(列出程序集中的所有类型);
Assembly a = AssemblyLoadFrom (ReflectionExampleexe);
Type[ ] mytypes = aGetTypes( );
Foreach (Type t in mytypes)
{
SystemConsoleWriteLine ( tName );
}
SystemConsoleReadLine ( );
SystemConsoleWriteLine (列出HellWord中的所有方法 );
Type ht = typeof(HelloWorld);
MethodInfo[] mif = htGetMethods();
foreach(MethodInfo mf in mif)
{
SystemConsoleWriteLine(mfName);
}
SystemConsoleReadLine();
SystemConsoleWriteLine(实例化HelloWorld并调用SayHello方法);
Object obj = ActivatorCreateInstance(ht);
string[] s = {zhenlei};
Object bojName = ActivatorCreateInstance(hts);
BindingFlags flags = (BindingFlagsNonPublic|BindingFlags
Public|BindingFlagsStatic|BindingFlagsInstance|BindingFlags
DeclaredOnly);
MethodInfo msayhello = htGetMethod(SayHello);
msayhelloInvoke(objnull);
msayhelloInvoke(objNamenull);
SystemConsoleReadLine();
}
}
}
using System;
namespace ReflectionExample
{
public class HelloWorld
{
string myName = null;
public HelloWorld(string name)
{
myName = name;
}
public HelloWorld() : this(null)
{}
public string Name
{
get
{
return myName;
}
}
public void SayHello()
{
if(myName == null)
{
SystemConsoleWriteLine(Hello World);
}
else
{
SystemConsoleWriteLine(Hello + myName);
}
}
}
}
在设计模式实现中使用反射技术
采用反射技术可以简化工厂的实现
()工厂方法通过反射可以将需要实现的子类名称传递给工厂方法这样无须在子类中实现类的实例化
()抽象工厂使用反射可以减少抽象工厂的子类
采用反射技术可以简化工厂代码的复杂程度在NET项目中采用反射技术的工厂已经基本代替了工厂方法
采用反射技术可以极大地简化对象的生成对以下设计模式的实现也有很大影响
()命令模式可以采用命令的类型名称作为参数直接获得命令的实例并且可以动态执行命令
()享元模式采用反射技术实例化享元可以简化享元工厂
委托技术与设计模式
委托技术是NET引入的一种重要技术使用委托可以实现对象行为的动态绑定从而提高设计的灵活性
NET中的委托技术
NET运行库支持称为委托的引用类型其作用类似于C++中的函数指针与函数指针不同委托实例独立于其封装方法的类主要是那些方法与委托类型兼容另外函数指针只能引用静态函数而委托可以引用静态和实例方法委托主要用于NET Framework中的事件处理程序和回调函数
所有委托都从SystemDelegate继承而来并且有一个调用列表这是在调用委托时所执行方法的一个链接列表产生的委托可以用匹配的签名引用任何方法没有为具有返回类型并在调用列表中包含多个方法的委托定义返回值
可以使用的委托Cimbine及Remove方法在其调用列表中添加和移除方法若要调用委托可使用Invoke方法或者使用BeginInvoke和EndInvoke方法异步调用委托委托类的实现由运行库提供而不由用户代码提供
委托适用于那种在某些语言中需要用函数指针来解决的情况但是与函数指针不同它是面向对象和类型安全的
委托声明定义一个类它是从SystemDelegate类派生的类委托实例封装了一个调用列表其中列出了一个或多个方法每个方法称为一个可调用实体对于实例方法可调用实体由一个实例和该实例的方法组成对于静态方法可调用实体仅由一个方法组成如果用一组合适的参数来调用一个委托实例则该委托实例所封装的每个可调用实体都会被调用并且使用上述同一组参数
委托实例的一个有用的属性是它既不知道也不关心其封装方法所属类的详细信息对它来说最重要的是这些方法与该委托的类型兼容即只要方法的返回类型和参数表是相同的则方法与委托类型兼容方法的名称不一定要与委托类相同
定义和使用委托分为声明实例化和调用个步骤委托用委托声明语法声明如
delegate void myDelegate( )
声明一个名为myDelegate的委托它不带参数并且不返回任何结果如
class Test
{
static void F( )
{
SystemConsoleWriteLine (TestF);
}
static void Main ( )
{
myeDelegate d = new myDelegate (F);
d ( );
}
}
创建一个myDelegate实例然后立即调用它这样做并没有太大的意义因为直接调用方法会更简单当涉及其匿名特性时委托才能真正显示出其效果如
void MultiCall (myDelegate d int count ) {
for (int I = ; I < count; I++) {
d( );
}
}
显示一个重复调用 myDelegate的MultiCall 方法这个方法不知道也不必知道myDelegate的目标方法的类型该方法具有的可访问性或者是否为静态对它来说最重要的是目标方法与myDelegate兼容
示例
下面的例子说明了委托的实现代码如下
using System;
namespace DelegateExample
{
public class TemplateMethod
{
public delegate float Comp(float afloat b);
public Comp myComp;
public TemplateMethod()
{}
public float DoComp(float[] f)
{
float nf = floatNaN;
foreach(float df in f)
{
if(floatIsNaN(nf))
nf = df;
else
nf = myComp(nfdf);
}
return nf;
}
}
}
委托技术与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();
wArea = new Darea(rcArea);
这样当调用w的Area时实际调用的是Reactangel的Area方法从实现意图上看NET的委托更好地实现了GOF所阐述的意图结构上也更为灵活但这两种委托解决的不是一个层面的问题GOF的委托强调的是一种策略而NET和委托技术则是具体实现
委托技术与设计模式实现
采用委托技术可以进一步实现用组合代替继承的思路很多采用继承实现的关系可以采用委托实现采用委托可以简化下列设计模式的使用
()模板方法这种方法采用继承实现具体方法采用委托可以动态实现方法的组合
()观察者可以使用事件委托实现观察者与主题之间的通信
()中介者使用委托可以去除工件与中介者之间的耦合关系