先看段代码:
Interface IMedia
{
Void Play();
Void Stop();
}
Public class Radio : IMedia
{
Void Play();{….}
Void Stop();{….}
}
Public class Recorder;: IMedia
{
Void Play();{….}
Void Stop();{….}
}
Public class MediaFactory
{
//取得所有播放设备
Public string[] GetDevicelst() {return new string[]{“Radio”,” Recorder”};
Public IMedia CreateDevice(string DeviceName)
{
If (DeviceName== “Radio”)
rturn new Radio();
else if (DeviceName== “Recorder”)
rturn new Recorder ();
elser
return null;
}
}
可见页面:
这段代码想必都很熟悉,这就不是个工厂模式么,没错,是个典型的工厂模式,而且还知道不少优点:
1. 采用了接口方式,统一使用IMedia来调用,而不会直接使用Radio或Recorder,省了不少代码,不再需要很多重复代码了;
2. 可以方便的扩展啊,比如我再新增一种播放设备的话,只要新增一个类,然后在MediaFactory中加上相应的代码就可以了啊,这对于使用者来说,可以不用修改代码了。
方便,确实方便很多了,但仔细想来啊,又有麻烦了,前面的程序已经开发好了,我要再加一个播放设备,加一个类的话,还得打开原先的代码,加上这个类,然后再MediaFactory中加上创建便个新类的方法,然后再编译后才能使用。
能不能简化呢,能不能我这个新的播放类,单独写一下,放到一个新的dll中,然后把这个dll往原先的exe目录中一放,然后就可以调用到这个新的播放类呢,这样我就可以不动原先的程序了呢?
答案是可以的:
Public string[] GetDevicelst() {return new string[]{“Radio”,” Recorder”};
看到这段代码,我们想到了,这个字符串不就可以保存在配置文件中吧,好,改之:
Public string[] GetDevicelst() {return Settings.Devicelist.Split(‘;’);}
在配置文件(exe.config)中:
<userSettings>
<WindowsFormsApplication1.Properties.Settings>
<setting name=" Devicelist " serializeAs="String">
<value> Radio ; Recorder </value>
</setting>
</WindowsFormsApplication1.Properties.Settings>
</userSettings>
好,取得设备列表的动态生成搞定了,新增一个类,也只要在这个配置文件里面加个,但看第二个方法CreateDevice,犯难了,在配置文件里面我保存的是类名的字符串,而在这个方法里面得返回其实例,得用new 类名()来创建对象。
这里我们在想是不是有这样一个方法,object instance = CreateInstance(“类名”),这样通过一个字符串,系统就给我返回相应的对象实例。
答案是有的,有这样的方法,这就是反射,我们看下怎么来用。
我们知道,在C#里面类型是随便取的,也就是就可以让两个类型相同,只不过在同一个程序里面,这两个相同的类得用不同的命空间里,在同一程序里,要表达一个类,得用命名空间.类名来表示。
但是在不同的dll中,还是会出现相同的命名空间和类名,所以对于全局来讲,要唯一表过一个类的话,得这样写命名空间.类名,程序集名。一般来说,这要样肯定也就够了,但时,不难保证,我的dll名称也可能相同啊,哦好,加个版本吧:命名空间.类名,程序集名, Version=版本号,那对于.net来说,还有一个多国语言支持呢,我同一批程序同一个版本,不同国家语言的包也可以相同啊,哦好,再加个语言限定吧:命名空间.类名,程序集名, Version=版本号, Culture=neutral这里面的neutral表示本地化的,就是自动读取操作系统的语言。如果再有相同的怎么办呢,没关系,我们可以在这个程序集上加一个guid啊,这个叫做强签名,这样,这个dll中的类就完全唯一了:命名空间.类名,程序集名, Version=版本号, Culture=neutral, PublicKeyToken=null/Guid值。OK,这就是一个类的全名的表示法,当然可以简写,”命名空间.类名,程序集名”,只要系统中没发现重复的就没事,发现重复的就要报错了的哦。
既然每个类可以用这个长长的字符串来定义的话,在framework中,就有这样一个表示类的类,叫做Type,每一个Type实例都唯一对应着一个类,那么Type对象怎么获取呢,肯定只有在dll(程序集)中找咯,又有一个对象Assembly,这个类是专门来表示程序集的,在这个Assembly中,有一个方法GetTypes,返回这个dll中的所有Type的集合。有了Type对象后就可很方便地创建其实例咯:
Assembly assembly = Assembly.LoadFile("xxxx.dll");
Type type = assembly.GetType("Radio");
object instance = Activator.CreateInstance(type); //创建实例
IMedia media = instance as IMedia;
这下好办了,可以能过字符串来取得对象了,好,将前面的代码改造下,连原来的配置文件都不要了:
Public class MediaFactory
{
//取得所有播放设备, 用hash表记录所有的设备列表,<名称,类型>
Public Hashtable GetDevicelst() {
Hashtable rtn = new Hashtable();
foreach (string file in Directory.GetFiles(Application.ExecutablePath, "*.dll"))
{
Assembly assembly = Assembly.LoadFile("xxxx.dll");
foreach (Type type in assembly.GetTypes())
{
//判断type是否继承IMedia
rtn.Add(type.Name, typ)
}
}
return rtn;
}
public IMedia CreateDevice(Type type)
{
Type type = assembly.GetType("Radio");
object instance = Activator.CreateInstance(type); //创建实例
return instance as IMedia;
}
}
看明白这段代码了吧,这样就实现了前面的要求,丢个dll到exe下面,就可以认出里面的IMedia类来并可调用了。
既然可以通过字符串来找到程序集,找到里面的类,还能创建具体的实例,那能不能继续,查找到类里有几个函数,有几个属性,然后能否再动态取得你这们实例里面的某个属性值,动态调用类里面的方法呢:
type.GetProperties();
type.GetMethods();
PropertyInfo prop;
prop.GetValue(obj);
MethodInfo method;
method.Invoke(obj, 参数);
看吧,这些方法够你遐想了,尽情地用到你可以简化代码的地方法吧!
反射,在类里面,反过来动态(运行时)找找类本身内部有哪些方法属性等结构!