我们在做机房收费系统的时候,我们利用抽象工厂+反射+配置文件来创建D层的引用,这样一来我们解耦了B层和D层。而如今,我们在ITOO中又遇到了,不知道大家有没有发现,我们B层和D层的生成路径都在WCFService的bin目录下,这是为什么哪?直到现在我才明白这样做是为了用反射。
一、 ITOO中用反射的好处
1、不用在WCFService层添加B层和D层的引用了(解耦)。
2、不用在WCFService层实例化B层与D层的引用,也就是不用New B层和D层类的实例, 动态的加载程序集,类、以及动态的使用类中的方法、属性等等。
二、反射的原理
通俗的说,我们将B层和D层生成的DLL放到WCFService层的Bin目录下,我们就可以不用实例化B层的类,而使用反射机制动态的使用这些DLL,还可以随便使用DLL饱含的一些类以及类中的一些方法,事件,属性,字段等等。
运行时,
程序集:*.exe, *.dll -> 加载到内存中 就是 Assembly对象
类:每个 class, interface -> 加载到内存中 就是 Type对象
类的成员:方法,字段,属性,事件等->加载到内存中也有相应的对象
三、反射的用途
1、使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。
2、使用 Module了解类似信息:包含模块的程序集以及模块中的类等。您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。
3、使用 ConstructorInfo了解如下的类似信息:构造函数的名称、参数、访问修饰符(如 public 或 private)和实现详细信息(如 abstract 或 virtual)等。
4、使用 Type 的GetConstructors 或 GetConstructor 方法来调用特定的构造函数。
5、使用 MethodInfo来了解如下的类似信息:方法的名称、返回类型、参数、访问修饰符(如 public 或 private)和实现详细信 息(如 abstract 或 virtual)等。使用 Type 的 GetMethods 或 GetMethod 方法来调用特定的方法。
6、使用 FieldInfo来了解如下的类似信息:字段的名称、访问修饰符(如 public 或 private)和实现详细信息(如 static)等;并获取或设置字段值。
7、使用 EventInfo来了解如下的类似信息:事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等;并添加或移除事件处理程序。
8、使用 PropertyInfo来了解如下的类似信息:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。
9、使用 ParameterInfo来了解如下的类似信息:参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。
四、对应用途的实例
我们先创建一个Uer类,这个User的命名空间是:ITOO_DAL,类中包括如下信息
public class User { public string Field="Hello World"; //字段 public string Name { get; set; } //属性 //无参构造函数 public User() { this.Name = "无参构造"; } //有参构造函数 public User(string name) { this.Name = name; } //public函数 public void PublishMethod() { Console.WriteLine(string.Format("反射调用一个public方法")); } //private函数 private void PrivateMethod() { Console.WriteLine(string.Format("反射调用一个Private方法")); } //static函数 public static string StaticMethod() { return "反射调用了一个Static方法"; } //public带参带返回值函数 public string PublishMethodReturn(string name) { return string.Format("反射调用一个public带参带返回值函数,参数为:{0}", name); } }
注意:一定要修改User类的生成路径,要生成到下面类库的Bin\Debug目录下,现在开始使用反射技术。
下面是一个UseReflection类,在该类中我们利用反射技术使用Use类编译生成的的DLL
class UserReflection { static void Main(string[] args) { //使用 Assembly 定义和加载程序集, //加载在程序集清单中列出的模块, //以及从此程序集中查找类型并创建该类型的实例. //获取程序集 Assembly assembly = Assembly.Load("ITOO_DAL"); Type type = assembly.GetType("ITOO_DAL.User");//从程序集中获取指定对象类型; var userNoParameter = Activator.CreateInstance(type);//使用Activator创建实例(无参数构造函数) var userParameter = Activator.CreateInstance(type, "小明"); //使用Activator创建实例(带参数构造函数) ConstructorInfo constructorNoParameter = type.GetConstructor(new Type[] { });//使用无参构造函数创建类(先反射创建构造函数,再使用构造函数创建类) var userNoParameterByConstructorInfo = constructorNoParameter.Invoke(new object[] { }); //使用有参构造函数创建类(先反射创建构造函数,再使用构造函数创建类) ConstructorInfo constructorParameter = type.GetConstructor(new Type[] { typeof(string) }); var userParameterByConstructorInfo = constructorParameter.Invoke(new object[] { "quwenzhe" }); //调用public函数(无参数) type.InvokeMember("PublishMethod", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, userNoParameter, null); //调用public函数(带参数) string returnValuePublic =type.InvokeMember("PublishMethodReturn", BindingFlags.InvokeMethod | BindingFlags.OptionalParamBinding, null, userNoParameter,new object[] { "新名字" }) as string; Console.WriteLine(returnValuePublic); //调用静态方法 string returnValueStatic = type.InvokeMember("StaticMethod", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new object[] { }) as string; Console.WriteLine(returnValueStatic); //反射属性 var Name = type.InvokeMember("Name", BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance, null, userNoParameter, new object[] { }) as string; Console.WriteLine(Name); //设置属性(设置Name属性为"NewName") type.InvokeMember("Name", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance, null, userNoParameter, new object[] { "NewName" }); //反射字段 string Field = type.InvokeMember("Field", BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance, null, userNoParameter, new object[] { }) as string; Console.WriteLine(Field); //设置字段(设置Field字段为"NewField") type.InvokeMember("Field", BindingFlags.SetField | BindingFlags.Public | BindingFlags.Instance, null, userNoParameter, new object[] { "NewField" }); }设用反射大致过程总结如下
第一步:使用 Assembly 定义和加载程序集,代码如下
Assembly assembly = Assembly.Load("ITOO_DAL")
第二步:从程序集中获取要操作的对象
Type type =assembly.GetType("ITOO_DAL.User")
第三步:创建类的构造函数,并用构造函数创建类
ConstructorInfo constructorNoParameter = type.GetConstructor(new Type[] { });//使用无参构造函数创建类(先反射创建构造函数,再使用构造函数创建类) var userNoParameterByConstructorInfo = constructorNoParameter.Invoke(new object[] { });
第四步:调用public方法(带参数)
string returnValuePublic =type.InvokeMember("PublishMethodReturn",BindingFlags.InvokeMethod |BindingFlags.OptionalParamBinding, null, userNoParameter,new object[] { "逗比,你看看!" }) as string;... ...
... ...
五、反射的优缺点
优点:使用反射提高了程序的灵活性和扩展性,降低了耦合度性,提高了自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
缺点:1、性能问题:使用反射基本上是一种解释操作,用于字段和方法的接入时要慢于直接代码,因此反射机制主要应用在灵活性和扩展性要求很高的系统框架上,普通的程序不建议使用。2、 使用反射会模糊程序内部逻辑:程序员需要在源代码中看到程序的逻辑,反射等绕过了源代码技术,因而会带来维护问题。反射代码比相应的直接代码复杂的多。
六、小结
以上是我对反射的理解,使用还不熟练,请广大朋友多多指教。