本章要点

  • 自定义特性
  • 反射

自定义特性运行把自定义元数据与程序元素关联起来。这些元数据时再编译过程中创建的并嵌入到程序集中。

反射是计算机术语,它描述在运行过程中检查和处理程序元素的功能。

反射允许完成以下任务:

  • 枚举类型的成员
  • 实例化新对象
  • 执行对象的成员
  • 查找类型的信息
  • 查找程序集的信息
  • 检查应用于某些类型的自定义特性
  • 创建和编译新程序集

程序的各个数据项上定义特性。这些特性都是Microsoft定义好的,作为.NET Framework类库的一部分,许多特性都得到了C#编译器的支持。对于这些特殊的特性,编译器可以以特殊的方式定制编译过程,例如,可以根据StructLayout特性中的信息在内存中布置结构。

.NET FrameWork 也允许用户定义自己的特性。显然,这些特性不会影响编译过程,因为这些编译器不能识别它们,但这些特性在应用于程序元素时,可以在编译好的程序集中用作元数据。

使用自定义特性非常强大的因素是使用反射,代码可以读取这些元数据,使用它们在运行期间作出决策。也就是说,自定义特性可以直接影响代码运行的方式。例如,自定义特性可以用于支持对自定义许可类进行声明性的代码访问安全检查,把信息与程序元素关联起来,程序元素由测试工具使用,或者在开发可扩展的架构时,允许加载插件或模块。

编写自定义特性

[FieldName("SocialSecurityNumber")]
public string SocialSecurityNumber
{
get { return ""; }
} [FieldNameAttribute("SocialSecurityNumber")]

当C#编译器发现这个属性(property)应用了 FieldName 特性时,首先会把字符串 Attribute 追加到这个名称的后面,形成一个组合名称 FieldNameAttribute ,然后在其搜索路径的所有名称空间(即在using语句中提及的名称空间)中搜索指定名称的类。但要注意,如果用一个特性标记数据项,而该特性的名称以字符串 Attribute 结尾,编译器就不会把该字符串加到组合名称中,而是不修改该特性名。

编译器会找到含有该名称的类,且这个类直接或间接派生自 System.Attribute。编译器还认为这个类包含控制特性用法的信息。特别是属性类需要指定:

  • 特性可以应用到那些类型的程序元素上(类、结构、属性和方法等)
  • 它是否可以多次应用到同一个程序元素上
  • 特性在应用到类或接口上时,是否由派生类和接口继承
  • 这个特性有哪些必选和可选参数

如果编译器找不到对应的特性类,或者找到一个这样的特性类,但使用特性的方式与特性类中的信息不匹配,编译器就会产生编译错误。例如,如果特类指定该特性只能应用于类,但我们把它应用到结构定义上,就会产生一个编译错误。

定义一个 FieldName 特性

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class FieldNameAttribute : Attribute
{
private string name; public FieldNameAttribute(string name)
{
this.name = name;
}
}

1.AttributeUsage特性

AttributeUsage主要用于标识自定义特性可以应用到那些类型的程序元素上。它这些信息由它第一个参数给出,其类型是枚举类型 AttributeTargets。AttributeTargets成员如下:

  成员名称 说明
  All

可以对任何应用程序元素应用属性。

  Assembly

可以对程序集应用属性。

  Class

可以对类应用属性。

  Constructor

可以对构造函数应用属性。

  Delegate

可以对委托应用属性。

  Enum

可以对枚举应用属性。

  Event

可以对事件应用属性。

  Field

可以对字段应用属性。

  GenericParameter

可以对泛型参数应用属性。(仅在 .net 2.0 以上提供。)

  Interface

可以对接口应用属性。

  Method

可以对方法应用属性。

  Module

可以对模块应用属性。

  Parameter

可以对参数应用属性。

  Property

可以对属性 (Property) 应用属性 (Attribute)。

  ReturnValue

可以对返回值应用属性。

  Struct

可以对结构应用属性,即值类型。

上述,两个成员不对应于任何程序元素 Assembly 和 Moudle。它们分别应用到 整个程序集 或 模块 中。这种特性可以放在源代码的任何地方,但需要用 Assembly 或 Moudle 作为前缀:

[assembly: SomeAssemblyAttribute(Parameters)]
[moudle: SomeAssemblyAttribute(Parameters)]

在指定自定义特性的有效目标元素时,可以使用按位 or 运算符,把这些值组合起来。

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]

当让也可以用 AttributeTargets.All 应用到所有类型的程序元素上。

AttributeUsage 特性还包含另外两个参数 AllowMultiple 和 Inherited 。

AllowMultiple 参数表示一个特性是否可以多次应用到同一项。如果把它设置为报错,下面编译器就会报错。

[FieldName("SocialSecurityNumber")]
[FieldName("NationalInsuranceNumber")]
public string SocialSecurityNumber
{

如果把 Inherited 参数设置为 true,就表示应用到类或接口上的特性也可以自动应用到所有派生的类或接口上。如果特性应用到方法或属性上,它就可以自动应用到该方法或属性等的重写版本上。

2.指定特性参数

指定自定义特性接受的参数,比如下述语句:

[FieldName("SocialSecurityNumber")]
public string SocialSecurityNumber
{
get { return ""; }
}

编译器会检查传递给特性的参数,并查抄该特性中带着些参数的构造函数。如果编译器找到一个这样的构造函数,编译器就会把指定的元数据传递给程序集。如果编译器找不到,就生成一个编译错误。如后面所述,反射会从程序集中读取元数据(特性),并实例化它们表示的特性类。因此,编译器需要确保存在这样的构造函数,才能在运行期间实例化指定的特性。

在本例中,仅为 FieldNameAttribute 类提供了一个构造函数,而这个构造函数有一个字符串参数。因此,在把 FieldName 特性应用到一个属性上时,必须为它提供一个字符串参数,如上面的代码所示。

如果可以选择特性提供的参数类型,就可以提供构造函数的不同重载方法,尽管一般是仅提供一个构造函数,使用属性来定义任何其他可选参数,下面将介绍可选参数。

3.指定特性的可选参数

在 ArrributeUsage 特性中,可以使用另一个中语法,把可选参数添加到特性中。这种语法指定可选参数的名称和值,它通过特性类中的公共属相或字段起作用。例如假定修改 SocialSecurityNumber属性的定义。如下所示:

[FieldName("SocialSecurityNumber", CodeComment = "This is the primary key field")]
public string SocialSecurityNumber
{

编译器识别第二个参数的语法<ParameterName>=<ParameterValue>,并且不会把这个参数传递给 FieldNameAttribute 类的构造函数,而是查找一个有该名称的公共属性或字段(最好不要使用公共字段,所以一般情况下要使用特性),编译器可以用这个属性设置第二个参数的值。所以必须给 FieldNameAttribute 修改如下

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class FieldNameAttribute : Attribute
{
private string name; private string comment; public FieldNameAttribute(string name)
{
this.name = name;
} public string Comment
{
get { return comment; }
set { comment = value; }
}
}

C# 反射 (15)的更多相关文章

  1. 【.Net】net 反射15分钟速成

    概述 什么是反射 Reflection,中文翻译为反射.        这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’.‘模块(Module)’. ...

  2. <NET CLR via c# 第4版>笔记 第15章 枚举类型和位标志

    15.1 枚举类型 枚举定义的符号是常量值. C#编译器编译时,会用数值替换符号,不再引用定义了符号的枚举类型.可能会出现一些版本问题. Enum.IsDefined(Type enumType, o ...

  3. C#高级编程笔记(11至16章)异步/托管/反射/异常

    11.1.2LINQ语句 LINQ查询表达式以from子句开始,以select或者group子句结束.在这两个子句之间可以跟零个或者多个from.let.where.join或者orderby子句. ...

  4. Python3学习之路

    python基础知识点 1.python基础知识点汇总 2.python常用数据类型 3.python之列表 4.python之字符串 5.python常用数据运算符 6.python之字典 7.py ...

  5. C#高级编程(第8版)

    http://spu.jd.com/11328513.html 第1章 .NET体系结构1.1 C#与.NET的关系1.2 公共语言运行库1.2.1 平台无关性1.2.2 提高性能1.2.3 语言的互 ...

  6. c#高级语言编程(第一部分)

    1.一步一步学c#(一):.NET体系结构 2.一步一步学c#(二):核心c# 3.一步一步学c#(三):对象和类型 4.一步一步学c#(四):继承 5.一步一步学c#(五):泛型 6.一步一步学c# ...

  7. C#高级编程第9版 阅读笔记(一)

    一.前言 C# 简洁.类型安全的面向对象的语言. .NET是一种在windows平台上编程的架构——一种API. C#是一种从头开始设计的用于.NET的语言,他可以利用.NET Framework及其 ...

  8. C#高级编程(第九版) 知识点梳理

    ---恢复内容开始--- 第二章 核心C# 2.7 命名空间可以使用别名,但是这样做有什么好处? 2.12 C#预处理器指令 #define DEBUG #if DEBUG Console.Write ...

  9. C#语言

    封面 书名 版权 前言 目录 第Ⅰ部分 C#语言 第1章  NET体系结构 1.1  C#与.NET的关系 1.2  公共语言运行库 1.2.1  平台无关性 1.2.2  提高性能 1.2.3  语 ...

随机推荐

  1. Js 集合运用

    1.给定一个单词good  要求输入 g1 o2 d1 (字母+加字母个数) 方法一: <script type="text/javascript"> var arrO ...

  2. 搜索(BFS、DFS、回溯)

    这类题是最简单的了都是一个套路,不像动态规划一类题一个套路,没做过就是不会也极难想出来. 一.BFS 解决的问题:用来初始点解决到指定点的最短路径问题,因为图的每一层上的点到初始点的距离相同.(注意是 ...

  3. (二)mybatis框架原理(图解)

    目录 mybatis 框架原理图(粗略版) mybatis 框架原理图(粗略版)

  4. Java手写简单HashMap一(包括增加,查找,toString,泛型)

    @Java 300 学习总结 HashMap底层采用实现采用了哈希表,结合了"数组和链表". 原理如图 一.定义HashMap类. 首先需要定义一个节点来存储数据,构成链表结构. ...

  5. spark调优篇-数据倾斜(汇总)

    数据倾斜 为什么会数据倾斜 spark 中的数据倾斜并不是说原始数据存在倾斜,原始数据都是一个一个的 block,大小都一样,不存在数据倾斜: 而是指 shuffle 过程中产生的数据倾斜,由于不同的 ...

  6. Educational Codeforces Round 71 (Rated for Div. 2) Solution

    A. There Are Two Types Of Burgers 题意: 给一些面包,鸡肉,牛肉,你可以做成鸡肉汉堡或者牛肉汉堡并卖掉 一个鸡肉汉堡需要两个面包和一个鸡肉,牛肉汉堡需要两个面包和一个 ...

  7. 怎样解决忘加new关键字所造成的问题

    通过构造函数 "new" 一个对象出来时, 如果忘记写这个 new, 那这个构造函数就不会返回一个实例对象, 而是会像普通函数一样执行. 下面是两种规避忘记写new时所引发的问题的 ...

  8. thinkphp 4.8 漏洞测试

    首先要部署环境 这里利用docker的方便部署性,来直接找个现成的    git clone https://github.com/vulnspy/thinkphp-5.1.29.git 下载安装后, ...

  9. 转载博客(Django2.0集成xadmin管理后台遇到的错误)

    转载博客地址:https://blog.csdn.net/yuezhuo_752/article/details/87916995 django默认是有一个admin的后台管理模块,但是丑,功能也不齐 ...

  10. Java Web-JSP学习

    Java Web-JSP学习 概念 Java Server Pages:Java服务器端页面.可以在其中直接定义HTML标签,也可以在其中直接定义java代码. 关于JSP和JAVASCRIPT的区别 ...