虽然还在学校读书,反射实际写的不多。但感觉反射在程序开发中用得还是挺多的,对我来说也是.NET中的一个难点。通过反射,我们可以在运行时获得.NET中的每一个类型的成员,这些类型包括类、结构、委托和枚举。有了反射,即可对每一个类型了如指掌。只要获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。
1.一个反射的例子
我们建立一个类库ClassPeople和一个控制台应用程序CA,下面的代码是ClassPeople类库里的People类和Man类
public class People
{
private string name = null;
public People() { }
public People(string strName)
{
name = strName;
}
public void Say()
{
if (name == null)
{
Console.WriteLine("hi~~");
}
Console.WriteLine("hi"+name);
}
}
public class Man
{
void Eat(){ Console.WriteLine("EAT"); }
}
下面的代码为CA项目里的Main方法。
static void Main(string[] args)
{
Type people=null;
//注意:执行这句代码时要将ClassPeople.dll复制到Debug目录下,这样才能找到这个文件
Assembly ass = Assembly.LoadFrom("ClassLibrary1.dll");
Type[] mytypes = ass.GetTypes();
Console.WriteLine("列出程序集中的所有类型");
foreach (Type t in mytypes)
{
Console.WriteLine(t.Name);
if (t.Name == "People")
{
people = t;
}
}
Console.WriteLine("列出People类中的方法:");
MethodInfo[] methodInfo = people.GetMethods();
foreach (MethodInfo mi in methodInfo)
{
Console.WriteLine(mi.Name);
}
//实例化People,并调用Say方法
Object obj = Activator.CreateInstance(people);
Object objName = Activator.CreateInstance(people, "kobe");
MethodInfo minfo = people.GetMethod("Say");
Console.WriteLine("调用Say方法");
minfo.Invoke(obj, null);
minfo.Invoke(objName, null);
Console.ReadLine();
}
2.反射常用类与方法
Assembly:通过这个类可以加载操纵一个程序集,并获取程序集内部信息。
EventInfo:这个类保存给定的事件信息。
FieldInfo:保存给定的字段信息。
MethodInfo:保存给定的方法信息。
ParameterInfo:保存参数信息。
PropertyInfo:保存属性信息。
Module:这个类可以使你能访问多个程序集中的给定模块。
Type类也是用的比较多,我们可以通过Type类得到一个类的内部信息或者反射创建一个对象。一般有三种方式去得到Type对象。
Type type=typeof(类名);
Type type=ExampleClass.GetType();
Type type=Type.GetType("要获取的类型的程序集限定名称");
我们还注意到Assembly加载程序集时有3个方法,分别是Load(),LoadFrom(),LoadFile()。这三个方法将目标托管程序集加载到当前应用程序域然后生成对应程序集实例。我们要知道Load是优先使用的,因为LoadFrom其实是将信息传给Load去实现,LoadFile则不会加载程序集引用的程序集。我在百度查资料时还发现使用LoadFrom可能还会出错,LoadFrom不能加载标识相同但路径不同的程序集,当使用LoadFrom时,和Load方法一样第一次加载程序集的信息无论失败还是成功都会被缓存,那么加载目录A中的程序集C.dll时则会保存记录,如果加载B目录下的C.dll时则会直接给出第一次缓存的程序集,也就是目录A中的C.dll。
3.通过反射实现工厂模式
class Program
{
static void Main(string[] args)
{
//使用ReflectFactory工厂去得到想要的类
IPeople xiaoHua = ReflectFactory.CreatePeopleByReflect("XiaoHua");
IPeople xiaoHon = ReflectFactory.CreatePeopleByReflect("XiaoHon");
xiaoHua.Eat();
xiaoHon.Eat();
Console.ReadLine();
}
}
//通过这个类得到要创建的类
public class ReflectFactory
{
public static IPeople CreatePeopleByReflect(string typeName)
{
Type type = Type.GetType("ConsoleApplication1."+typeName);
ConstructorInfo ci = type.GetConstructor(System.Type.EmptyTypes);
return (IPeople)ci.Invoke(null);
}
} //定义了XiaoHua和XiaoHon2个类和它们的父接口IPeople
public interface IPeople
{
void Eat();
}
public class XiaoHua : IPeople
{
public void Eat()
{
Console.WriteLine("小花吃2个梨子");
}
}
public class XiaoHon : IPeople
{
public void Eat()
{
Console.WriteLine("小红吃3个苹果");
}
} //下面是不用反射的写法。这样的话就要为每个类写一个创造类或创造的方法,比如要得到XiaoHua类我们要再写一个GetXiaoHua类,
//如果又有新成员加入,那岂不是还要再新增类或方法,而采用反射则可以随便添加,只需要实现IPeople接口即可,这就是我体会到的反射的好处。
public interface FactoryPeople
{
IPeople GetPeople();
}
public class GetXiaoHua : FactoryPeople
{
public IPeople GetPeople()
{
return new XiaoHua();
}
}
public class GetXiaoHon : FactoryPeople
{
public IPeople GetPeople()
{
return new XiaoHon();
}
}