通过这篇文章,不仅可以了解到Attribute的工作原理,还可以了解到GetcustomeAttribute是的内部执行流程。最后,你会看到,使用缓存机制可以极大的优化反射Attribute的性能。

本文结构:

  1.为什么在对象上标记Attribute性能很慢。

  2.编译器如何编译带有Attribute标记的类型

  3.定义解析器,在运行时获取并解析对象上的Attribute

  4.GetCustomeAttributes方法的工作原理

  5.优化Attribute

  6.Attribute性能优化完整代码

  7.总结

参考资料:

关于Attribute的缓存思想请参见后面的链接,当然这篇文章里还介绍了依赖注入以及ORM中的一些优化,链接如下:http://www.codeproject.com/Articles/503527/Reflection-optimization-techniques

关于CustomeAttribute的介绍请参见ECMA-CLI文档第二部分Partition_II_Metadata的第22章第10节,但我再后面还是给出了这个介绍。

1.为什么在对象上标记Attribute性能很慢。

首先请看如下代码:

   [TableAttribute("student")]

    class StudentModel {

        public string Id{get;set;}

    }

    class TableAttribute : Attribute {

        string name;

        public string Name {

            get { return name; }

        }

        public TableAttribute(string name) {

            this.name = name;

        }

    }

性能损耗1:每创建一个StudentModel对象,都要利用反射创建一个TableAttribute对象。

性能损耗2:上一步骤中创建的所有TableAttribute都是完全相同的,值都是”student”。换句话说,如果创建了n 个StudentModel,那么就会具有n个完全一样的TableAttribute。

2.编译器如何编译带有Attribute标记的类型

编译器在编译上述代码时,发现StudentModel有一个TableAttribute特性,就会在最终的程序集中生成一个customeAttribute元数据表,这个表有三个字段:parent,type,value。其中parent指向StudentModel类,type指向TableAttribute的构造函数,value值是要传递给TableAttribute的参数,这个里的参数就是”student”。如下图:

3.定义解析器,在运行时获取并解析对象上的Attribute

如果对象上仅仅是标记了Attribute,那么是不会有什么性能损失的,因为我们并没有使用它。

每当我们自定义一个Attribute时,都要同时创建一个用于解析这个Attribute的解析器。这个解析器需要做两件事:第一获取对象上标记的Attribute,其次根据Attribute中的属性值来执行相应的动作。代码如下:

 class AttributeInterpreter {

        public static void Interprete() {

            TableAttribute[] attributes = typeof(StudentModel).GetCustomAttributes(typeof(TableAttribute), true) as TableAttribute[];

            foreach(var eachAttribute in attributes) {

                string tableName = eachAttribute.TableName;

                //根据tableName访问数据库中的对应表,然后读取数据并创建StudentModel对象。

            }

        }

    }

这里,首先获取StudentModel的Type,然后调用Type的GetCustomeAttributes方法,并传递了typeof(TableAttribute)参数。最后GetCustomeAttributes方法返回StudentModel上标记的TableAttribue对象。

附注:ECMA-CLI中的CustomeAttribute详细说明:

  .     C us to mAt tr i b ut e :  x0 C
The CustomAttribute table has the following columns:
? Parent (an index into any metadata table, except the CustomAttribute table itself; more precisely,
a HasCustomAttribute (§24.2.) coded index)
? Type (an index into the MethodDef or MemberRef table; more precisely, a CustomAttributeType
(§24.2.) coded index)
? Value (an index into the Blob heap)
Partition II
The CustomAttribute table stores data that can be used to instantiate a Custom Attribute (more precisely, an
object of the specified Custom Attribute class) at runtime. The column called Type is slightly misleading—it
actually indexes a constructor method—the owner of that constructor method is the Type of the Custom
Attribute.
A row in the CustomAttribute table for a parent is created by the .custom attribute, which gives the value of
the Type column and optionally that of the Value column (§).

ECMA-CLI CustomeAttribute简介

4.GetCustomeAttributes方法的工作原理

在运行时,当调用GetCustomeAttributes时,CLR会遍历customeAttribute元数据表,查找parent=StudentModle和type=TableAttribute的CustomeAttribute元数据表。找到之后,根据type字段找到TableAttribute,然后创建他的实例,调用构造函数,并为构造函数传递参数“student”。

5.优化Attribute

优化的第一步就是利用缓存机制来存储已经创建好的TableAttribute,当需要对象时直接从缓存里取,这样只有在第一次创建StudentModel时,才会反射创建TableAttribute对象。如下

 private static Dictionary<Type, TableInfo> cache = new Dictionary<Type, TableInfo>();
public StudentModel() {
Type studentModelType = this.GetType();
Type tableAttType=typeof(TableAttribute);
if(!cache.TryGetValue(tableAttType, out tableINfo)) {
TableAttribute[] tableAttributes = studentModelType.GetCustomAttributes(typeof(TableAttribute), true) as TableAttribute[]; if(tableAttributes == null)
return; TableAttribute tableAttribute = tableAttributes[];
cache.Add(tableAttType, new TableInfo(tableAttribute.TableName));
}

使用缓存需要考虑锁的问题。当几个线程同时修改缓存时,必须保证在同一时刻只有一个线程修改,这里使用双锁机制来进行线程同步。如下:

 public StudentModel() {
Type studentModelType = this.GetType();
Type tableAttType=typeof(TableAttribute); TableInfo tableInfo;
if(!cache.TryGetValue(tableAttType, out tableInfo)) {
lock(this) {
if(!cache.TryGetValue(tableAttType, out tableInfo)) {
TableAttribute[] tableAttributes = studentModelType.GetCustomAttributes(typeof(TableAttribute), true) as TableAttribute[]; if(tableAttributes == null)
return; TableAttribute tableAttribute = tableAttributes[];
cache.Add(tableAttType, new TableInfo(tableAttribute.TableName));
}
}
}
}

优化的结果:只在第一次创建对象时反射Attribute,减少了内存的使用。

6.Attribute性能优化完整代码

 [Table("student")]
class StudentModel {
public string Id{get;set;}
//定义缓存,存储已经创建好的TableAttribute对象。
private static Dictionary<Type, TableInfo> cache = new Dictionary<Type, TableInfo>();
public StudentModel() {
Type studentModelType = this.GetType();
Type tableAttType=typeof(TableAttribute); TableInfo tableInfo;
if(!cache.TryGetValue(tableAttType, out tableInfo)) {
lock(this) {
if(!cache.TryGetValue(tableAttType, out tableInfo)) {
TableAttribute[] tableAttributes = studentModelType.GetCustomAttributes(typeof(TableAttribute), true) as TableAttribute[]; if(tableAttributes == null)
return; TableAttribute tableAttribute = tableAttributes[];
cache.Add(tableAttType, new TableInfo(tableAttribute.TableName));
}
}
}
}
}
//AlloMultiple为true,表明这个特性不能被重复标记,原因很简单,一个实体对象不能映射到两个表。
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false)]
class TableAttribute : Attribute {
string tableName; public string TableName {
get { return tableName; }
}
public TableAttribute(string name) {
this.tableName = name;
}
}

Attribute性能优化完整代码

7.总结

在使用Attribute特性时,编译器会在最终的程序集中生成一个CustomeAttribute元数据表,这个表实际上是一个映射表,其中的type字段和parent字段将特性和对象相关联。

仅仅定义个Attribute是没有任何实际作用的,我们需要定义一个解析器用于获取对象上的Attribute并根据其属性值采取相应的动作。

在运行的时候,想要获取对象上的某特性时,需要调用Type的GetCustomeAttributes方法。这个方法在内部使用了反射来获取特性并实例化特性。

由于反射很浪费性能,并且对于类型的每一个对象,与其关联的Attribute都是相同的,所以可以利用字典只保存其中的一个。由于反射很浪费性能,所以可以考虑将创建好的Attribute缓存起来。

如果这个缓存需要被多个线程修改,需要使用锁来同步,这里为了提供性能,使用了双锁机制和Monitor。

优化特性(Attribute)性能的更多相关文章

  1. php8.0正式版新特性和性能优化学习

    前言 PHP团队宣布PHP8正式GA(链接).php的发展又开启了新的篇章,PHP8.0.0版本引入了一些重大变更及许多新特性和性能优化机制.火速学习下~ JIT(Just in Time Compi ...

  2. 特性attribute,声明和使用attribute,应用attribute,AOP面向切面,多种方式实现AOP

    1 特性attribute,和注释有什么区别2 声明和使用attribute3 应用attribute4 AOP面向切面5 多种方式实现AOP ---------------------------- ...

  3. [C#] C# 知识回顾 - 特性 Attribute

    C# 知识回顾 - 特性 Attribute [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5911289.html 目录 特性简介 使用特性 特性 ...

  4. .Net内置特性Attribute介绍

    特性Attribute概述 特性(Attribute)是一种特殊的类型,可以加载到程序集或者程序集的类型上,这些类型包括模块.类.接口.结构.构造函数.方法.字段等,加载了特性的类型称之为特性的目标. ...

  5. 通过 SMB 直通优化文件服务器的性能

    https://technet.microsoft.com/zh-cn/library/hh831487.aspx Windows Server 2012 内置新增功能,称为 SMB 直通,用来支持使 ...

  6. java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

    java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较 ...

  7. Angular2 VS Angular4 深度对比:特性、性能

    欢迎大家持续关注葡萄城控件技术团队博客,更多更好的原创文章尽在这里~~​ 在Web应用开发领域,Angular被认为是最好的开源JavaScript框架之一. Google的Angular团队已于3月 ...

  8. Redis基础用法、高级特性与性能调优以及缓存穿透等分析

     一.Redis介绍 Redis是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库.缓存服务或消息服务使用.Redis支持多种数据结构,包括字符串.哈希表.链表.集合.有序集合.位图.Hype ...

  9. web页面内容优化管理与性能技巧

    来源:GBin1.com 回 想一下,以前我们不得不花费大量时间去优化页面内容(图片.CSS等等),如今用户有更快速的互联网链接,我们似乎能够使用更大的图像或更大的闪 存文件,里面包含的有视频或者图片 ...

随机推荐

  1. wireshark使用

    http://jingyan.baidu.com/article/7f41ececede744593c095c79.html

  2. App 开发:Hybrid 架构下的 HTML5 应用加速方案

    在移动 App 开发领域,主流的开发模式可分为 Native.Hybrid.WebApp 三种方式.然而 2013 年,纯 WebApp 开发模式的发展受到一定挫折,以 Facebook 为代表的独立 ...

  3. Python 自然语言处理(1) 计数词汇

    Python有一个自然语言处理的工具包,叫做NLTK(Natural Language ToolKit),可以帮助你实现自然语言挖掘,语言建模等等工作.但是没有NLTK,也一样可以实现简单的词类统计. ...

  4. 无废话Android之listview入门,自定义的数据适配器、采用layoutInflater打气筒创建一个view对象、常用数据适配器ArrayAdapter、SimpleAdapter、使用ContentProvider(内容提供者)共享数据、短信的备份、插入一条记录到系统短信应用(3)

    1.listview入门,自定义的数据适配器 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/and ...

  5. 栈与队列:refresh的停车场

    数据结构实验之队列一:排队买饭 Time Limit: 1000MS Memory limit: 65536K 题目描述 中午买饭的人特多,食堂真是太拥挤了,买个饭费劲,理工大的小孩还是很聪明的,直接 ...

  6. 关于python装饰器(Decorators)最底层理解的一句话

    一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...

  7. 用Node.js开发Windows 10物联网应用

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 未来10年内,物联网将会如移动互联网这样深入到我们生活的各方各面.所以微软现在对物联网进行了 ...

  8. HR外包系统 - 客户员工 发薪需求/个税需求 设置

    最好,客户公司层面进行设置,如果单一情况,只需要设置公司,如果不是单一情况,设置员工, 另外员工只能从公司设置好的地方选择过来. 增强系统简便设置和设置的灵活性.

  9. 在Eclipse中用图形界面的方式获取Salesforce中Object的Query语句

    对Salesforce中的Object进行相应的Query是必不可少的操作,大家可以去这个链接去看看官网的解读  http://docs.database.com/dbcom/en-us/db_sos ...

  10. 【myEcplise2015 更换主题+字体颜色】

    更换myEcplise样式: 若对js文件或者java文件中的字体颜色不是很满意,可以去按照这个路径去更新字体颜色: 以javaScript文件为例子: 修改完成之后,javascript文件中文字是 ...