.net反射存在的问题及优化技术--反射之读取特性Attributes的改进

.net反射存在的问题及优化技术--反射之读取特性Attributes的改进

2315发表于2016-08-27

特性为程序集、类、或者类的成员提供额外信息的,其实特性也是一个特殊的类。如果一个类中加了特性,那么每次创建这个类的对象都会去找、并应用这些相关的特性类。因此最好是把这特性相关值用静态变量存起来,第一次实例化之后就缓存起来,这样只有第一次创建对象用到了反射

下面是一个实体类:

[Table(Name="Employees")]
public class Employee : Entity
{
    [PrimaryKey]
    public int Id { get; set; }
    
    public string Name { get; set; }
    public string Address { get; set; }
    public DateTime DOB { get; set; }

} 

上面代码是一个实体类对应到数据库中一名为”Employees“的表,可以看到这个类的Id属性加一个特性PrimaryKey,加上这个特性表示这个一字段其特殊的意义,这样每次实例化对象都会用到这个特性。为了能够获取类成员的特性信息,我们就得用到反射利用这个类来读取特性,但是这样做代价是很大,因为这个类的每个对象都会重复这样的步骤。一个比较好的处理方式是在第一次创建对象的读取类的特性信息,然后把这读取到的缓存起来,接下来的对象就用再重复这些耗性能的操作了。

public class Entity
{
    public Entity()
    {
        Type curType = this.GetType();

        // 下面的反射代码在每次创建对象都会执行
        object[] tableAttributes =
		curType.GetCustomAttributes(typeof(TableAttribute), true);

        if(null != tableAttributes && tableAttributes.Count() > 0)
        {
            // 获取特性信息
        }

        // 使用这些特性信息,做一些自定义的操作...
    }
}

上面使用反射的缺点&劣势:

1、这个类每次创建新对象都会执行利用反射来动态获取特性信息代码。

2、利用反射来获取特性信息代价是非常大的。

针对上面的缺点进行优化

声明一个结构体来包含信息

public struct TableInfo
{
    public string TableName;
    public string PrimaryKey;
} 

我们用一静态的字典集合变量来存储这些特性信息,把类的Type作为字典的Key,当一次创建类的对象时在字典里面是找的,就会利用反射找到其特性,然后存到字典中缓存里面,这样接下来的创建这个类型的对象就用重复的利用反射来找到类的特性,而直接从缓存中取。


public class Entity
{
    private static Dictionary<Type, TableInfo> tableInfoList = 
                                         new Dictionary<Type, TableInfo>();

    public Entity()
    {
        Type curType = this.GetType();
        TableInfo curTableInfo;
        
        if (!tableInfoList.TryGetValue(curType, out curTableInfo))
        {
            lock (this)
            {
                // double check双重检查来保证线程安全的单例对象
                if (!tableInfoList.TryGetValue(curType, out curTableInfo))
                {
                    object[] tableAttributes =
			curType.GetCustomAttributes(typeof(TableAttribute), true);

                    if(null != tableAttributes && tableAttributes.Count() > 0)
                    {
                        curTableInfo = new TableInfo();
                        curTableInfo.TableName = ((TableAttribute) tableAttributes[0]).Name;
			tableInfoList.Add(curType,curTableInfo);
                    }
                }
            }
        }

        // use curTableInfo here 
    }
}


double check双重检查是为了这段代码在线程的情况下也能保证在内存中只有一个这样的集合。

利用这上面技术改进反射读取特性Attributes的优点&优势

1、获取特性信息的反射代码只有在第一次的时候执行并们们保存了一份复本在内存中,在接下来创建同一个类的对象都可用。

2、利用缓存将反射的性能开销最小化。 

小编蓝狐