转:.NET特性与反射

.NET编译器的任务之一是为所有定义和引用的类型生产元数据描述。除了程序集中标准的元数据外,.NET平台允许程序员使用特性(attribute)把更多的元数据嵌入到程序集中。简而言之,特性就是用于类型(比如类、接口、结构等)、成员(比如属性、方法等)、程序集或模块的代码注释。

当浏览.NET命名空间时,将发现许多预定义特性,可以在应用程序中使用它们。此外,可以创建自定义特性,通过从Attribute派生出新类型进一步修饰类型的行为。当在代码中应用特性时,如果它们没有被另一个软件显示地反射,那么嵌入的元数据基本没什么作用。反之,嵌入程序集的元数据介绍将被忽略不计,而并无害处。

限制特性使用:有时候需要建立这样一个自定义特性,它只能被应用到选定的代码元素上。如果希望限制自定义特性的应用范围,需要在自定义特性的定义中应用[AttributeUsage]特性。[AttributeUsage]特性支持AttributeTargets枚举值得任意组合(通过OR操作)。

自定义特性:你可以随时创建自己

 声明一个特性:和C#的大多数元素一样,特性是由类来实现的。要创建一个自定义特性,你须要从System.Attribute类派生你新的自定义特性的类。

public class BugFixAttribute:System.Attribute

你须要告诉编译器这个特性可以被用在那种类型的元素上(特性目标)。使用 特性可以说明这一信息。

[AttributeUsage(AttributeTargets.Class|

AttributeTargets.Constructor|

AttributeTargets.Field|

AttributeTargets.Method|

AttributeTartets.Property|

AllowMultiple=true)]

AttrbuteUsage特性是一个应用在特性上的特性,也就是一个元特性。也可以说,它提供了元-元数据,也就是于元数据相关的数据。你可以给AttributeUsage传递两个参数。

第一个参数是一个标志集合,它指明了特性的目标类型,在这个例子中,它们分别是类和 构造函数、字段、方法和属性。第二个参数是一个用来指明特定的元素是否可以接受多个这样的特性的 标记。在这个例子中,AllowMultiple被设置为True,这表明了类的成员可以应用多个BugFixAttribute特性。

构造一个特性:特性可以接受两种类型的参数:位置参数命名参数。在BugFix特性的例子中,程序员的名字、Bug ID 和日期都是位置型参数,而备注是命名参数。位置型参数是通过构造函数传入的。它们必须按照构造函数中声明的顺序传入。

[csharp] view plaincopy

  1. public BugFixAttribute(int bugID, string programmer, string date)
  2. {
  3. this.BugID = bugID;
  4. this.Programmer = programmer;
  5. this.Date = date;
  6. }

命名参数是使用字段或属性的形式来实现的:

public string Comment { get; set; }

通常也可以为位置参数创建只读属性:

public int BugID { get; private set; }

使用特性

定义好特性之后,通过将它放在目标的前面,就可以让它起作用了。为了测试前一示例的 BugFixAttribute特性,以下的程序创建一个名为MyMath的简单类,并为它提供了两个函数。另外,还给这个类设置了BugFixAttribute特性以记录代码维护的历史信息:

[BugFixAttribute(121,"Jesse Liberty","01/03/08")]

[BugFixAttribute(107,"Jesse Liberty","01/04/08",Commet="Fixed off by one errors")]

public class MyMath

这些特性值将被存储到元数据中。

[csharp] view plaincopy

  1. [AttributeUsage(AttributeTargets.Class|
  2. AttributeTargets.Constructor|
  3. AttributeTargets.Field|
  4. AttributeTargets.Module|
  5. AttributeTargets.Property,
  6. AllowMultiple=true)]
  7. public class BugFixAttribute:System.Attribute
  8. {
  9. //具有特定位置參數的特性構造函數
  10. public BugFixAttribute(int bugID, string programmer, string date)
  11. {
  12. this.BugID = bugID;
  13. this.Programmer = programmer;
  14. this.Date = date;
  15. }
  16. //访问器
  17. public int BugID { get; private set; }
  18. public string Programmer { get; private set; }
  19. public string Date { get; private set; }
  20. //命名参数的属性
  21. public string Comment { get; set; }
  22. }
  23. //************将特性赋给类****************
  24. [BugFixAttribute(121,"Jesse Liberty", "01/03/08")]
  25. [BugFixAttribute(107, "Jesse Liberty", "01/04/08", Comment = "Fixed off by one errors")]
  26. public class MyMath
  27. {
  28. public double DoFunc1(double param1)
  29. {
  30. return param1 + DoFunc2(param1);
  31. }
  32. public double DoFunc2(double param1)
  33. {
  34. return param1 / 3;
  35. }
  36. }

[csharp] view plaincopy

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. MyMath mm = new MyMath();
  6. Console.WriteLine("Calling DoFunc(7).Result:{0}", mm.DoFunc1(7));
  7. Console.Read();
  8. }
  9. }

反射(Reflection)

近年来,Reflection已经成为主流语言中必备的特色,其主要用途是执行时期提供类型信息,一旦拥有这些信息,设计人员可以轻易地创建出具备动态解析能力的应用程序,例如在执行时期以一个字符串创建起对应的对象,抑或是以一个字符串来调用函数,都可以由Reflection技术来达成。Reflection技术同时也是RAD开发工具的幕后功臣,运用Reflection技术,RAD开发工具可以取出某个组件的属性与事件等信息显示于属性表之上。在某些特殊应用上,Reflection更是扮演着极关键的角色,例如设计人员可以用Reflection取得某个类的信息,再搭配.NET的CodeDOM技术来产生一个继承至该类的类源代码,动态为其实现某个接口,或是覆写某个函数,抑或是结合Script语言来产生一个符合特定结构需求的对象。.NET Framework,Reflection是经由Type对象来操作,其中分成两部分,一部分是提供该Type本身的信息,例如Public、Sealed\Serializable、Attributes、Interfaces等等。此部分还算相当直观,此处就不在赘述,另一部分则是取得该Type内的成员信息如字段GetField(s)     
、属性GetProperty、事件GetEvent(s) 、Attributes(GetCustomAttibutes  ),这一系列函数的返回值皆是MemberInfo类或其子嗣

MemberInfo类的子嗣:

FieldInfo类代表成员变量,PropertyInfo类代表属性,EventInfo类代表事件,MethodBase类细分为两部分,ConstructorInfo类代表创建函数,MethodInfo类代表成员函数。

有趣的是Type类本身也是MemberInfo类的子嗣,这种设计代表着Nested Type(外围类型)也是Member的一员。

下面列举Type类型的常用方法:

函数                                                       说明

GetConstructor(s)                  取得此类型的创建函数,其将回传一个ConstructorInfo对象或数组

GetField(s)                              取得此类型中成员变量,其将回传一个FiledInfo对象或数组

GetMember(s)                         取得此类中的成员,其类型可以是变量、事件、属性、方法及Nested Type,其将回传一个MemberInfo对象或数组

GetEvent(s)                             取得此类型中的事件,其将回传一个EventInfo对象或数组

GetProperty/GetProperties        取得此类型中的属性,其将回传一个PropertyInfo对象或数组

GetNestedType(s)                  取得声明于此类型内类型,其将回传一个Type对象或数组

GetCustomAttibutes                   取得绑定于此类型的Attitudes

利用这些函数,设计人员可以在执行时期取得某个类型中所有的成员信息,也可以在非默认对象类型的情况下调用其成员函数或是设定某属性值。

 

[csharp] view plaincopy

  1. /// <summary>
  2. /// 成员存取器工具类
  3. /// </summary>
  4. public static class MemberAccessorUtils
  5. {
  6. private static readonly IMemberAccessor[] EMPTY = new IMemberAccessor[0];
  7. /// <summary>
  8. /// Gets the accessors.
  9. /// </summary>
  10. /// <param name="type">The type.</param>
  11. /// <param name="bindingFlags">The binding flags.</param>
  12. /// <returns></returns>
  13. public static IMemberAccessor[] GetMemberAccessors(Type type, BindingFlags bindingFlags)
  14. {
  15. if (type == null || type == typeof(object))
  16. return EMPTY;
  17. FieldInfo[] fieldInfos = type.GetFields(bindingFlags);
  18. PropertyInfo[] propertyInfos = type.GetProperties(bindingFlags);
  19. IMemberAccessor[] memberAccessors = new IMemberAccessor[fieldInfos.Length + propertyInfos.Length];
  20. int i = 0;
  21. foreach (FieldInfo fieldInfo in fieldInfos)
  22. {
  23. memberAccessors[i++] = new FieldAccessor(fieldInfo);
  24. }
  25. foreach (PropertyInfo propertyInfo in propertyInfos)
  26. {
  27. memberAccessors[i++] = new PropertyAccessor(propertyInfo);
  28. }
  29. return memberAccessors;
  30. }
  31. /// <summary>
  32. /// Gets the accessor.
  33. /// </summary>
  34. /// <param name="type">The type.</param>
  35. /// <param name="name">The name.</param>
  36. /// <param name="bindingFlags">The binding flags.</param>
  37. /// <returns></returns>
  38. public static IMemberAccessor GetMemberAccessor(Type type, string name, BindingFlags bingdingFlags)
  39. {
  40. FieldInfo fieldInfo = type.GetField(name, bingdingFlags);
  41. if (fieldInfo != null)
  42. return new FieldAccessor(fieldInfo);
  43. PropertyInfo propertyInfo = type.GetProperty(name, bingdingFlags);
  44. if (propertyInfo != null)
  45. return new PropertyAccessor(propertyInfo);
  46. return null;
  47. }
  48. }

要让元数据中的特性真正起作用,你需要一种访问它们的方法,并且最好是在程序运行的时候。在Reflection命名空间中的类和System.Type类一起为你提供了检查和处理元数据的功能。

反射通常被用在以下4种任务中

查看元数据:这一功能可能会被希望显示元数据的工具和辅助程序使用。

执行类型发现功能:这一功能允许你检查程序集里的类型并处理或实例化这些类型。在创建自定义脚本时这一功能会非常有用。例如,你可能希望允许用户使用一种脚本语言和你的程序打交道,这种脚本语言包括Javascript或你自己创建的一种语言。

延迟绑定到方法和属性上:这一功能允许程序员基于类型发现的功能来调用动态实例化对象上的属性和方法。这也被称为动态调用。

在运行时创建类型(反射代码发射功能):反射最强大的用途是在运行时创建新的类型,然后使用这些类型执行任务。当一个任务在运行时创建的自定义类和运行速度比在编译期创建的更加通用的代码快得多的时候,你可能会这样做。

 查看元数据

下面通过发射读取MyMath类中的元数据。一开始,你须要获得一个MemberInfo类的对象。位于System.Reflection命名空间的这一对象的作用在于发现成员的特性,并提供访问元数据的方法:

System.Reflection.MemberInfo inf = typeof(MyMath);

调用MyMath类型上typeof操作符将返回一个Type类型的对象,Type类型派生自MemberInfo类型。Type类是反射类的核心,它封装了对象类型的表达形式。Type类是访问元数据的主要方法。它派生自MemberInfo类并且封装了关于类的成员的信息(如方法、属性、字段、事件等信息)。

下一步就是调用这个MemberInfo对象上的GetCustomAttributes方法,传入你希望查找的特性的类型。你将获得一个对象的数据,数据中每一项的类型都是BugFixAttribute:

object[] attributes;

attributes = inf.GetCustomAttributes(typeof(BugFixAttribute), false);

[csharp] view plaincopy

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. MyMath mm = new MyMath();
  6. Console.WriteLine("Calling DoFunc(7).Result:{0}", mm.DoFunc1(7));
  7. //获得成员信息并使用它获取自定义特性
  8. System.Reflection.MemberInfo inf = typeof(MyMath);
  9. object[] attributes;
  10. attributes = inf.GetCustomAttributes(typeof(BugFixAttribute), false);
  11. //迭代访问特性,并获取属性
  12. foreach (Object attribute in attributes)
  13. {
  14. BugFixAttribute bfa = (BugFixAttribute)attribute;
  15. Console.WriteLine("\nBugID: {0}", bfa.BugID);
  16. Console.WriteLine("Programmer: {0}", bfa.Programmer);
  17. Console.WriteLine("Date: {0}", bfa.Date);
  18. Console.WriteLine("Comment: {0}", bfa.Comment);
  19. }
  20. Console.Read();
  21. }
  22. }

类型发现(Type DisCovery)

你可以使用反射功能来浏览并检查程序集里的内容。可以查找与模块相关的类型,于类型相关的方法、字段、属性和事件,以及类型中所用方法的签名、类型支持的接口和类型的基类等信息。

首先,你须要使用Assembly.Load()静态方法动态地加载一个程序集。Assembly类封装了实际的程序集自身,它提供了反射的功能。Load方法的其中一种签名如下:

public static Assembly.Load(AssmeblyName)

在下面例子中,你将核心库的名称传给了Load()方法。Mscorlib.dll包含了.NET框架的核心类:Assembly a = Assembly.Load("Mscorlib");

加载了程序集之后,你就可以调用GetTypes()方法,让它返回Type对象的数组。Type对象是反射功能的核心。它表示了类型的声明信息(其中包括类、接口、数组、值和枚举等):

Type[] types = a.GetTypes();

你可以使用一个Foreach循环显示出从程序集返回的类型的数组。

[csharp] view plaincopy

  1. namespace ReflectingAnAssembly
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. //程序集里面有什么内容
  8. Assembly a = Assembly.Load("Mscorlib");
  9. Type[] types = a.GetTypes();
  10. foreach (Type t in types)
  11. {
  12. Console.WriteLine("Type is {0}", t);
  13. }
  14. Console.WriteLine("{0} types found", types.Length);
  15. Console.Read();
  16. }
  17. }
  18. }

反射类型

你也可以通过反射获得Mcsorlib程序集里的某一个类型。要实现这一功能,你可以使用typeOf或GetType()方法从程序集里获取一个类型。

Type theType = Type.GetType("System.Reflection.Assembly");
            Console.WriteLine("\nSingle Type is {0}\n", theType);
            Console.Read();

输出结果是:

Single Type is System.Reflection.Assembly

查找所用类型成员

你可以使用Type类的GetMembers()方法获得Assembly类型的所用成员,例如下面例子。这个 方法将会列出所有得方法、属性和字段。

[csharp] view plaincopy

  1. Type theType = Type.GetType("System.Reflection.Assembly");
  2. Console.WriteLine("\nSingle Type is {0}\n", theType);
  3. //获得所有成员
  4. MemberInfo[] mbrInfoArray = theType.GetMembers();
  5. foreach (MemberInfo mbrInfo in mbrInfoArray)
  6. {
  7. Console.WriteLine("{0} is a {1}", mbrInfo, mbrInfo.MemberType);
  8. }
  9. Console.Read();

输出的结果很长,但是在输出的结果中,你会看到字段、方法、构造函数和属性。

查找类型的方法

你可能会希望只关注于方法,而排除字段、属性等其他类型的信息。要实现着以目标,你可以去掉对GetMembers()方法的调用。

MemberInfo[] mbrInfoArray = theType.GetMembers();

然后添加对GetMethods()方法的调用: mbrINnfoArray = theType.GetMethods();

现在输出的结果中只包含方法:

查找特定的类型成员

最后如果你希望进一步缩小范围,你可以使用FindMembers方法找到类型的特定成员。例如,你可以将 搜索范围缩小到只包括名称以"Get"开头的方法。

要缩小搜索范围,你须要使用FindMembers方法,这个方法接受4个参数:

转:http://blog.csdn.net/byondocean/article/details/6802111

时间: 2024-10-07 23:36:21

转:.NET特性与反射的相关文章

Day07 jdk5.0新特性&Junit&反射

day07总结 今日内容 MyEclipse安装与使用 JUnit使用 泛型 1.5新特性 自动装箱拆箱 增强for 静态导入 可变参数方法 枚举 反射 MyEclipse安装与使用(yes) 安装MyEclipse 先安装了JDK ? MyEclipse介绍 ? MyEclipse是Eclipse的一个插件: MyEclipse是需要花钱的: MyEclipse官网不在欢迎中国人登录: ? MyEclipse使用 ? 1 创建项目 选择工作空间: 工作空间路径不能有空格和中文: 工作空间以班名

黑马程序员——java高新---JDK1.5新特性和反射

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.JDK1.5新特性 ——>静态导入 import和import static之间的区别: 1.import是导入一个类或某个包中所有的类. 2.import static是导入一个类中的某个静态成员或所有的静态成员. 注意: 1.当导入的两个类中有同名成员时,需要在成员前加上相应的类名. 2.当类名重名时,需要指定具体的包名. 3.方法重名时,需要指定具体所属的对象或者类. 代码示例: 1

特性与反射形成sql语句 一

最近需要使用sql语句查询数据库 但是不想每次都写数据库读写所以查了下反射 就想使用反射出来某个实体的所有属性,然后根据属性查询并赋值 首先,需要一个实体类才能反射出数据库对应的字段, 但是开始写属性的时候,我看见特性蛮好用的,可以实现切面编程. 我也查了特性,但是网上这方面资料不多,大多都是介绍,只能自己慢慢摸索了. [Model.Context.SelectContext] public class Entity { public int ID { get; set; } public st

黑马程序员——java高新技术(新特性、反射、泛型)

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- java高新技术 第一部分——JDK1.5新特性 1.增强for循环 格式: for(元素类型 变量名 : Collection集合 & 数组 ) { }//增强for循环括号里写两个参数,第一个是声明一个变量,第二个就是需要迭代的容器 高级for循环和传统for循环的区别: 高级for循环在使用时,必须要明确被遍历的目标.这个目标,可以是Collection集合或者数组,如果遍历Collec

C#-特性,反射,动态编程

参考文档 https://www.cnblogs.com/SignX/p/11569837.html 特性 侵删https://www.cnblogs.com/forever-Ys/p/10428568.html 一.什么是特性 特性是一种允许我们向程序的程序集添加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类.简单说就是为一个类或者类的属性等添加更多的说明与标记 MSDN中对它的解释是:特性提供功能强大的方法以将声明信息与 C# 代码(类型.方法.属性等)相关联.特性与程序实体关

Java高级特性之反射学习总结

老规矩我们还是先提出几个问题,一门技术必然要能解决一定的问题,才有去学习掌握它的价值 一. 什么是反射? 二.反射能做什么? 一. 什么是反射? 用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods. 如果你是一个Android Developer,前辈们都会教导你尽量少用反射,效率太低,

.NET技术-1.0.使用反射、特性简化代码(验证Model类)

使用反射.特性简化代码 参考项目:利用反射验证Model类/AssemblyVerification 假设现在有一个学生类(Student) /// <summary> /// 学生类 /// </summary> public class Student { /// <summary> /// 名字 /// </summary> private string name; public string Name { get { return name; } s

Java反射-再次认识

最近的学习发现在很多方面,基础知识掌握的还很不牢固,所以对于架构.知识点等属于那种问啥啥知道,做啥啥不出来的那种类型.前些日子,老师一直在抓基础,做什么都要从最简单的demo开始,只有懂了原理之后再去用一些高深的东西如框架等才会理解的更深刻.现在首先需要理解的就是基本上每个Java框架都在用的反射技术. 要想理解反射,首先得了解类的加载过程,看下图: 我们的源代码经过编译之后变成字节码,然后在JVM中运行时通过类加载器加载字节码在内存中生成Class类对象,这个Class类对象内包含有field

2017年2月28日-----------乱码新手自学.net 之特性与验证

现在看asp.net MVC5自学已经到了第六章:数据注解与验证. 话得从以前看MVC music store(音乐商店项目)的源码说起, 最初看music store源码完全就是一脸懵逼,整个程序,找了半天,只看到控制器有少许逻辑代码,例如编辑专辑的视图里面,用户输入的title到底符不符合规范, VIEW里面即无相关验证的JS代码,又没有进行后台的数据判断. 1 @using (Html.BeginForm()) { 2 @Html.ValidationSummary(true) 3 <fi