.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 和日期都是位置型参数,而备注是命名参数。位置型参数是通过构造函数传入的。它们必须按照构造函数中声明的顺序传入。

  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

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

  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. }
  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

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

 

  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);

  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循环显示出从程序集返回的类型的数组。

  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类型的所用成员,例如下面例子。这个 方法将会列出所有得方法、属性和字段。

  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

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

  1. C#特性和反射

    C#特性和反射 .NET编译器的任务之一就是为所有定义和引用的类型生成元数据描述.除了程序集中标准的元数据外,.NET平台还支持特定(attribute)把更多的元数据嵌入到程序集中. .NET特性扩 ...

  2. 利用特性和反射给泛型Model赋值

    为了解决从数据库读取的表字段和自己建的viewModel字段名称不相符的问题 本人小白,初次将特性及反射应用到实例,写的不好的地方还请大家多多包涵 新建一个控制台应用程序命名为ReflectAndAt ...

  3. EF+LINQ事物处理 C# 使用NLog记录日志入门操作 ASP.NET MVC多语言 仿微软网站效果(转) 详解C#特性和反射(一) c# API接受图片文件以Base64格式上传图片 .NET读取json数据并绑定到对象

    EF+LINQ事物处理   在使用EF的情况下,怎么进行事务的处理,来减少数据操作时的失误,比如重复插入数据等等这些问题,这都是经常会遇到的一些问题 但是如果是我有多个站点,然后存在同类型的角色去操作 ...

  4. 详解C#特性和反射(四)

    本篇内容是特性和反射的最后一篇内容,前面三篇文章: 详解C#特性和反射(一) 详解C#特性和反射(二) 详解C#特性和反射(三) 一.晚期绑定(Late Binding)是一种在编译时不知道类型及其成 ...

  5. 反射简介—C#特性和反射

    .NET编译器的任务之一就是为所有定义和引用的类型生成元数据描述.除了程序集中标准的元数据外,.NET平台还支持特定(attribute)把更多的元数据嵌入到程序集中. .NET特性扩展了抽象的Sys ...

  6. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  7. Javaweb学习笔记——(七)——————myexlipse基本使用、jdk5.0新特性及反射讲解

    1.debug调试模式: *使用这种模式,调试程序(看到程序运行停止在这一行) -显示出来行号 -双击左边,出现一个圆点,表示设置了一个断点 *使用debug as方式,运行程序 -特使是否进入到调试 ...

  8. 详解C#特性和反射(一)

    使用特性(Attribute)可以将描述程序集的信息和描述程序集中任何类型和成员的信息添加到程序集的元数据和IL代码中,程序可以在运行时通过反射获取到这些信息: 一.通过直接或间接的继承自抽象类Sys ...

  9. 详解C#特性和反射(三)

    类型信息(Type Information)用来表示类型声明的信息,通过抽象基类System.Type的实例存储这些信息,当使用反射时,CLR获取指定类型的Type对象,通过这个对象即可访问该类型的任 ...

  10. 详解C#特性和反射(二)

    使用反射(Reflection)使得程序在运行过程中可以动态的获取对象或类型的类型信息,然后调用该类型的方法和构造函数,或访问和修改该类型的字段和属性:可以通过晚期绑定技术动态的创建类型的实例:可以获 ...

随机推荐

  1. .NET开发笔记--对config文件的操作(2)

    1.前台代码 sys_channel_edit.aspx <tbody id="item_box"> <asp:Repeater ID="rptList ...

  2. Java Something

    Java静态代码检查工具 FindBugs FindBugs is a defect detection tool for Java that uses static analysis to look ...

  3. 源码分析——Action代理类的工作

     Action代理类的新建 通过<Struts2 源码分析——调结者(Dispatcher)之执行action>章节我们知道执行action请求,最后会落到Dispatcher类的serv ...

  4. Adapter适配器 final int Id 导致选中的Item不在当前界面

    写了上面这么一个横向混动,点击切换到,哪个的Item上就会有一个  常用  的小图标.但是我每次滑动切换到后面   成龙9这个Item,这个 常用的图片,也在 这个上面了,但是他一更新,就变成 等你再 ...

  5. git GUI 入门

    一:安装一个git 及gui 二:配置gui及线上的git链接 在Git Gui中,选择Remote->add添加远程服务器,远程服务器信息有两种填写方式,填写https地址或ssh地址,对应g ...

  6. 160727、自定义hibernate主键生成策略生成字符串+数字自增长

    需求:需要自增长注解如MyId0001.MyId0002.MyId0003 实现:实现这个接口org.hibernate.id.IdentifierGenerator 一.MyIdGenerator. ...

  7. SharePoint服务器端对象模型 之 使用LINQ进行数据访问操作(Part 4)

    (六)高效合理的使用LINQ 1.DataContext中的两个属性 为了能够使用DataContext进行数据提交,在DataContext进行数据查询和操作的过程中,内部会进行数据状态的保持和追踪 ...

  8. 巨蟒python全栈开发-第20天 核能来袭-约束 异常处理 MD5 日志处理

    一.今日主要内容 1.类的约束(对下面人的代码进行限制;项目经理的必备技能,要想走的长远) (1)写一个父类,父类中的某个方法要抛出一个异常 NotImplementedError(重点) (2)抽象 ...

  9. Super Jumping! Jumping! Jumping!---hdu1087(动态规划)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1087 题意就是给你n个数,找出某个序列的最大和,这个序列满足依次增大的规则: 哎,这个题之前做过,但是 ...

  10. 深入理解CNI

    1.为什么会有CNI? CNI是Container Network Interface的缩写,简单地说,就是一个标准的,通用的接口.已知我们现在有各种各样的容器平台:docker,kubernetes ...