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 语 ...
随机推荐
- JS获取URL地址
var url = window.location.href;
- storm drpc分布式本地和远程调用模式讲解
一.drpc 的介绍 1.rpc RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. 2.drpc drp ...
- [转帖]IIS7配置Gzip压缩
IIS7配置Gzip压缩 https://www.cnblogs.com/coce/p/6804373.html II7中自带了gzip功能,理论上应该比ii6配置起来应该简单一点,但是容易出的问 ...
- [转帖].NET导出Excel的四种方法及评测
.NET导出Excel的四种方法及评测 https://www.cnblogs.com/sdflysha/p/20190824-dotnet-excel-compare.html 导出Excel是.N ...
- DOS命令学习
DOS命令学习 一.DOS使用常识 DOS的概况 DOS(Disk Operating System)是一个使用得十分广泛的磁盘操作系统,就连眼下流行的Windows9x/ME系统都是以它为基础. 常 ...
- codeforces 1244C (思维 or 扩展欧几里得)
(点击此处查看原题) 题意分析 已知 n , p , w, d ,求x , y, z的值 ,他们的关系为: x + y + z = n x * w + y * d = p 思维法 当 y < w ...
- vue的基本语法
在学习vue之前,我们应了解一下什么是vue.js? 什么是Vue.js? Vue.js是目前最后一个前端框架,React是最流行的一个前端框架(react除了开发网站,还可以开发手机App,Vue语 ...
- linux安装字体库(simSun为例)
在开发过程中,发现报表打印,字体和默认字体不一样.本地开发环境(windows)没问题,但是远程linux一直出现这样的问题.经过排查发现linux没有安装中文字体库宋体. linux查看当前的字体库 ...
- 一个SDL2.0程序的分析
//把图片加载到SDL_Texture SDL_Texture* loadTexture(const std::string &file, SDL_Renderer *ren){ ...
- asp.net mvc 导出Excel
[HttpGet] public void ExportNissan(string CheckListNo) { JObject queryParam; if (CheckListNo == null ...