C# 反射 (15)
本章要点
- 自定义特性
- 反射
自定义特性运行把自定义元数据与程序元素关联起来。这些元数据时再编译过程中创建的并嵌入到程序集中。
反射是计算机术语,它描述在运行过程中检查和处理程序元素的功能。
反射允许完成以下任务:
- 枚举类型的成员
- 实例化新对象
- 执行对象的成员
- 查找类型的信息
- 查找程序集的信息
- 检查应用于某些类型的自定义特性
- 创建和编译新程序集
程序的各个数据项上定义特性。这些特性都是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)的更多相关文章
- 【.Net】net 反射15分钟速成
概述 什么是反射 Reflection,中文翻译为反射. 这是.Net中获取运行时类型信息的方式,.Net的应用程序由几个部分:‘程序集(Assembly)’.‘模块(Module)’. ...
- <NET CLR via c# 第4版>笔记 第15章 枚举类型和位标志
15.1 枚举类型 枚举定义的符号是常量值. C#编译器编译时,会用数值替换符号,不再引用定义了符号的枚举类型.可能会出现一些版本问题. Enum.IsDefined(Type enumType, o ...
- C#高级编程笔记(11至16章)异步/托管/反射/异常
11.1.2LINQ语句 LINQ查询表达式以from子句开始,以select或者group子句结束.在这两个子句之间可以跟零个或者多个from.let.where.join或者orderby子句. ...
- Python3学习之路
python基础知识点 1.python基础知识点汇总 2.python常用数据类型 3.python之列表 4.python之字符串 5.python常用数据运算符 6.python之字典 7.py ...
- 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 语言的互 ...
- c#高级语言编程(第一部分)
1.一步一步学c#(一):.NET体系结构 2.一步一步学c#(二):核心c# 3.一步一步学c#(三):对象和类型 4.一步一步学c#(四):继承 5.一步一步学c#(五):泛型 6.一步一步学c# ...
- C#高级编程第9版 阅读笔记(一)
一.前言 C# 简洁.类型安全的面向对象的语言. .NET是一种在windows平台上编程的架构——一种API. C#是一种从头开始设计的用于.NET的语言,他可以利用.NET Framework及其 ...
- C#高级编程(第九版) 知识点梳理
---恢复内容开始--- 第二章 核心C# 2.7 命名空间可以使用别名,但是这样做有什么好处? 2.12 C#预处理器指令 #define DEBUG #if DEBUG Console.Write ...
- C#语言
封面 书名 版权 前言 目录 第Ⅰ部分 C#语言 第1章 NET体系结构 1.1 C#与.NET的关系 1.2 公共语言运行库 1.2.1 平台无关性 1.2.2 提高性能 1.2.3 语 ...
随机推荐
- Linux中 cmake-3.x 编译安装以及man page添加
首先回顾一下 cmake-2.x 的编译安装. ================ cmake-2.x编译安装说明 ================编译安装的命令: ./bootstrap --pref ...
- [CF798D]Mike and distribution_贪心
Mike and distribution 题目链接:http://codeforces.com/problemset/problem/798/D 数据范围:略. 题解: 太难了吧这个题..... 这 ...
- ROS的初步学习--创建一个工作空间和一个程序包
快速开始 创建工作区(workspace) 工作区可以作为一个独立的项目进行编译,存放ROS程序的源文件.编译文件和执行文件.建立工作区的方法如下: mkdir -p ~/catkin_ws/src ...
- Linux 多命令语句与重定向
多命令语句 Linux中我们在shell输入命令一般是一条一条执行,但是我们同样可以用一行语句写出多命令,下面就举出几个常见的方法 “;”分号用法 方式:command1 ; command2 用;号 ...
- Graphite简要教程
转载自DevOps实战:Graphite监控上手指南 英文原文Getting Started with Monitoring using Graphite 英文原文Google快照 作者 Frankl ...
- python+django学习四
1.setting.py中设置好 STATIC_URL = '/static/'STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')] 前端文件存 ...
- C++性能榨汁机之伪共享
C++性能榨汁机之伪共享 来源 http://irootlee.com/juicer_false_sharing/ 前言 在多核并发编程中,如果将互斥锁的争用比作“性能杀手”的话,那么伪共享则相当于 ...
- JS写斐波那契数列的几种方法
斐波那契数,指的是这样一个数列:1.1.2.3.5.8.13.21.……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字 ...
- windows下Jmeter压测端口占用问题(亲测有效)
windows下Jmeter压测端口占用问题 1 报错信息描述 压测的初期,在设置了 150qps/s 的并发数下压测几分钟后 Jmeter 就出现了如下报错. JAVA.NET.BINDEXCEPT ...
- ant-design的Table组件自定义空状态
Table,是antd中一个我们经常使用的组件,在官方文档中给出了非常详细的实例以及API, 但在我们在使用过程中可能需要根据项目的要求来定制空状态时的展示. 什么是空状态呢? 在antd的官方文档中 ...