[.net 面向对象程序设计进阶] (20) 反射(Reflection)(上)利用反射技术实现动态编程
本节导读:本节主要介绍什么是.NET反射特性,.NET反射能为我们做些什么,最后介绍几种常用的反射的实现方法,通过对反射性特的了解,可以设计出非常有用的基于反射的编程模式。
读前必备:
[.net 面向对象编程基础] (10) 类的成员(字段、属性、方法)
1.什么是.NET反射?
反射是.NET一个重要的特性,《CLR via C#》一书中对.NET反射的解释为:在我们应用程序中使用元数据来表示存储。编译程序集或模块时,编译器会创建一个类型定义表、一个字段定义表、一个方法定义表以及其它表。而我们如果想动态调用这些元数据表(或说是解析这些表),或说是为这些元数据创建一个对象模型,这个过程就是反射。
简单通俗的说,就是动态调用编译过的程序集中的属性,字段,方法等的过程,就是反射。
反射在.NET,通过System.Reflection命名空间中的类来实现。
2.反射能为我们做些什么?
这个问题是我们学习反射的重点,总得知道学习它的好处,才会继续把本文看下去。
反射特性,确实是.NET一个非常重要且有用的特性。
A.枚举类型成员
B.实例化新对象
C.执行对象成员
D.查找类型信息
E.查询程序集信息
F.检查应用于某种类型的自定义特性
G.创建和编译新的程序集
H.简化执行时而非编译时绑定的数据的操作。(C# 4.0以后新功能)
此外.NET新版本中允许泛型上的反射.
以上是反射的基本特性,参考了《C#本质论》和《C#高级编程》
基于上面的基本特性,我们可以设计出很多非常实用的编程模式。
下面列举几种基于反射设计模式下的实例:
A.利用反射创动态创建程序集的API文档。基于反射允许枚举程序集中类型及成员的特性,我们可以通过反射获取已编译的程序集中的字段方法属性事件和他们的XML注释。从而动态创建程序集的API文档;
B.非常常用的反射工厂模式。反射工厂模式在设计模式中比较容易理解,也比较简单。很多代码生成器中就利用这种设计模式完成不同数据库的反射调用。比如我们有MsSql、MySql、Oracle这三种数据库,在项目设计中,我们有可能随时换另一种数据库(当然这只是假设),因此需要同时实现这三种数据库的基础增删改查的类即数据访问类。我们要切换数据库的时候,只需要在config中更改数据库类型,其他的工作交给反射工厂类去动态调用编译好的程序集中对应的数据库访问方法。
如果没有理解也没关系,这里只是说明一下反射的应用实例,以便于更有信心的学习反射。反射在设计模式中的应还有很多,这里不再列举。
3.反射应用基础
上面说了这么多,无非就是先让我们理解反射能为我们做些什么,下面进入正题,说一下反射的代码实现。
下面主要介绍反射的核心类及类成员
反射的命名空间:System.Reflection
反射的类大多都在这个命名空间中。
主要的类: System.Type
这个类是反射的核心,其属性方法可以得到运行时的信息。
Type类派生于System.Reflection.MemberInfo抽象类
MemberInfo类中的只读属性 |
||
属性 |
描述 |
备注 |
Type DeclaringType |
获取声明该成员的类或接口的类型 |
|
MemberTypes MemberType |
获取成员的类型,这个值用于指示该成员是字段、方法、属性、事件、或构造函数 |
这是一个枚举,它定义了用于表示不同成员的类型值。这些值包括:MemberTypes.Constructor, MemberTypes.Method, MemberTypes.Field, MemberTypes.Event, MemberTypes.Property。因此可以通过检查MemberType属性来确定成员的类型,例如,在MemberType属性的值为MemberTypes.Method时,该成员为方法 |
Int MetadataToken |
获取与特定元数据相关的值 |
|
Module Module |
获取一个代表反射类型所在模块(可执行文件)的Module对象 |
|
String Name |
成员的名称 |
|
Type ReflectedType |
反射的对象类型 |
Type类的只读属性 |
|
属性 |
描述 |
Assembly Assembly |
获取指定类型的程序集 |
TypeAttributes Attributes |
获取制定类型的特性 |
Type BaseType |
获取指定类型的直接基类型 |
String FullName |
获取指定类型的全名 |
bool IsAbstract |
如果指定类型是抽象类型,返回true |
bool IsClass |
如果指定类型是类,返回true |
string Namespace |
获取指定类型的命名空间 |
Type类的方法 |
|
方法 |
描述 |
ConstructorInfo[] GetConstructors() |
获取指定类型的构造函数列表 |
EventInfo[] GetEvents(); |
获取指定类型的时间列 |
FieldInfo[] GetFields(); |
获取指定类型的字段列 |
Type[] GetGenericArguments(); |
获取与已构造的泛型类型绑定的类型参数列表,如果指定类型的泛型类型定义,则获得类型形参。对于正早构造的类型,该列表就可能同时包含类型实参和类型形 |
MethodInfo[] GetMethods(); |
获取指定类型的方法列表 |
PropertyInfo[] GetProperties(); |
获取指定类型的属性列表e |
MemberInfo[] GetMembers(); |
获取指定类型的成员列表 |
反射还有很多类,这里不一一介绍,详细可以查阅MSDN:
https://msdn.microsoft.com/zh-cn/library/system.reflection.aspx
4.反射实例
下面通过一个实例来学习一下反射最基本的使用方法。
建立一个解决方案,包含两个项目,项目ClassLibrary生成一个DLL(包含两个类),另一个项目是ReflectionTestGet,用于反射调用类ClassLibrary
第一个项目的两个类如下:
MartialArtsMaster.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ClassLibrary
{
/// <summary>
/// 类:武林高手
/// MartialArtsMaster
/// </summary>
class MartialArtsMaster
{
/// <summary>
/// 级别
/// </summary>
public int _level = ;
/// <summary>
/// 编号
/// </summary>
public int Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 门派
/// </summary>
public string Menpai { get; set; }
/// <summary>
/// 武学
/// </summary>
public string Kungfu { get; set; }
/// <summary>
/// 级别
/// </summary>
public int Level
{
get
{
return _level;
} set
{
_level = value;
}
} /// <summary>
/// 攻击
/// </summary>
/// <param name="kungfu"></param>
/// <returns></returns>
public string Attack(string kungfu)
{
return "使用用了功夫:" + kungfu;
} public string Kill(string kungfu, string name)
{
return "使用用了功夫:" + kungfu + "击杀了" + name;
}
}
}
Person.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ClassLibrary
{
/// <summary>
/// 类:人
/// </summary>
class Person
{
public string gender { get; set; }
public string race { get; set; }
public string Country { get; set; }
public string Eat(string strCountry)
{
switch (strCountry)
{
case "美国": return "爱吃西餐"; case "韩国": return "爱吃泡菜";
default:
return "不知道";
}
}
}
}
第二个项目调用如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection; namespace ReflectionTestGet
{
class Program
{
static void Main(string[] args)
{ Assembly asm = Assembly.LoadFrom("ClassLibrary.dll"); //加载指定的程序集
Type[] alltype = asm.GetTypes(); //获取程序集中的所有类型列表
foreach (Type calssName in alltype)
{
Console.WriteLine("加载程序的集类名:"+ calssName.Name); //打印出程序集所有类
foreach (var field in calssName.GetFields())
Console.WriteLine(calssName.Name+"字段有:" + field.Name); //打印出程序集所有字段,注意只能获取公有字段
foreach (var pro in calssName.GetProperties())
Console.WriteLine(calssName.Name + "属性有:" + pro.Name); //打印出程序集所有属性
foreach (var met in calssName.GetMethods())
Console.WriteLine(calssName.Name + "方法有:" + met.Name); //打印出程序集所有方法 }
Console.ReadKey();
}
}
}
运行结果如下:
5.本节要点:
本节主要介绍和反射的用途及反射的基本操作类及属性方法,下节继续深入介绍如何将反射技术应用于实际项目之中。
==============================================================================================
<如果对你有帮助,记得点一下推荐哦,如有
有不明白或错误之处,请多交流>
<对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>
<转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>
.NET 技术交流群:467189533
==============================================================================================