.NET Reflection optimization 反射优化

23 篇文章 0 订阅

原文地址:http://www.codeproject.com/Articles/503527/Reflection-optimization-techniques

简介

反射是 Microsoft .NET framework 中非常强大的特性,它在 System.Reflection 命名空间中提供了一整套在运行时动态载入/处理程序集的 API。

反射 (Reflection) 如此强大,但仍需谨慎使用,因为它实际上是一种用性能换扩展性的一种方法,这里提供了一些优化反射的方法,以期把对性能的影响最小化。

场景1(动态调用一个方法)

一个标准且完整的动态调用方法示例

public void DynamicExecution()
{
    Type objType = Type.GetType("DynamicExecution.Test.DynamicClass");

    object obj = Activator.CreateInstance(objType);
    MethodInfo mInfo = objType.GetMethod("AddNumbers",
	new Type[] { typeof(int), typeof(int) });

    // 动态调用 obj 对象上的 AddNumbers 方法
    mInfo.Invoke(obj, new object[] { 1, 5 });

    PropertyInfo pInfo = objType.GetProperty("TEST_ID");

    // 动态设置 obj 对象的 ID 属性
    pInfo.SetValue(obj, "TEST_ID", null );
} 

缺点:

  1. 每次动态调用方法(或属性)将比静态调用方式慢好几倍
  2. 编译时方法/属性的传入参数的类型安全性无法检查,在运行时传入错误数据/类型就会挂掉
  3. 编码量大,且可读性不高

优化代码

Public void OptimizedDynamicExecution()
{
    Type objType = Type.GetType("DynamicExecution.Test.DynamicClass");

    IDynamicClass obj = Activator.CreateInstance(objType) as IDynamicClass;

    if (null != obj)
    {
        // 静态调用 obj 对象上的 AddNumbers 方法
        int result = obj.AddNumbers(1, 5);

        // 静态设定 obj 对象的 ID 属性
        obj.ID = 10; 
    }
}

// 在 OptimizedDynamicExecution API 中使用的 Interface
public interface IDynamicClass
{
    int ID { get; set; }
    int AddNumbers(int a, int b);
} 

改进点

  1. 静态调用提高了执行速度
  2. 引入一个 interface 实现了类型安全
  3. 该 interface 能在其他场景使用
  4. 更短的代码,更好的可读性

场景2(读取自定义属性custom attributes)

标准代码示例

[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; }

}

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

        object[] tableAttributes = curType.GetCustomAttributes(typeof(TableAttribute), true);

        if(null != tableAttributes && tableAttributes.Count() > 0)
        {
            // 获取 attribute 信息
        }
        // 使用 attribute 信息 
    }
}

以上定义了一个 Employee 实体类,对应数据库中的表明为 “Employees”,另外它的 Id 属性标记了一个 PrimaryKey attribute。
为了获取这些定义在 custom attributes 中的信息,在父类中使用了反射。

缺点

  1. 每次实例化都通过反射来获取 custom attributes 信息
  2. 通过反射来获取 custom attributes 信息是个“重活”

优化代码

// 追加一个 C# 结构来存储信息
public struct TableInfo
{
    public string TableName;
    public string PrimaryKey;
}

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)
            {
                // 使用双重检查来降低 lock 消耗
                if (!tableInfoList.TryGetValue(curType, out curTableInfo))
                {
                    object[] tableAttributes = curType.GetCustomAttributes(typeof(TableAttribute), true);

                    if(null != tableAttributes && tableAttributes.Count() > 0)
                    {
			// 缓存中没有时,新建一个 TableInfo
                        curTableInfo = new TableInfo();
                        curTableInfo.TableName = ((TableAttribute) tableAttributes[0]).Name;
			// 其他处理
			
			//把新建的 TableInfo 加入缓存
			tableInfoList.Add(curType, curTableInfo);
                    }
                }
            }
        }
        // use curTableInfo here 
    }
}

这里没有对反射操作进行修改,而是通过对反射的信息进行缓存从而减少反射操作的次数来提高程序的执行效率

优点

  1. 每种类型仅在初次使用时需要反射
  2. 通过缓存最小化反射的使用次数

场景3(依赖注入 Dependency Injection)

如今的程序都强调低耦合,允许在运行时动态载入配置文件中指定的程序集/模块,这通常都使用到反射并因此降低了运行效率。

Microsoft .NET 轻量级代码生成可以解决“创建设计时未知类型对象”的问题,它在 System.Reflection.Emit 命名空间下提供了多个 API。

这个技术能减少反射的过度使用,并且通过缓存方法代理的方式来提高后续调用的执行效率。

// 依赖的协议接口
public interface ISecurityProvider
{
    bool ValidateUser(string userId, string password);
    List<User> GetUsersList();
}

// 依赖的具体实现类
public class DefaultSecurityProvider : ISecurityProvider
{
    public bool ValidateUser(string userId, string password)
    {
        ...
    }
    public List<User> GetUsersIist()
    {
        ...
    }
}

// 动态实例化 DefaultSecuirtyProvider 的方法
private void CreateInstance()
{
    Type classType = Type.GetType("DefaultSecurityProvider, AssemblyName");
    
    // 使用反射动态创建一个实例对象
    ISecurityProvider employeeInstance = 
        Activator.CreateInstance(classType) as ISecurityProvider;
}

缺点

  1. 每次都使用反射来创建实例对象
  2. 通过反射来实例化对象是个“重活”

优化代码

// 动态创建实例(不使用反射)
private void CreateInstance()
{
    Type classType = Type.GetType("DefaultSecurityProvider, AssemblyName");

    // 获取一个指定对象的实例化方法的代理 delegate
    CreateInstanceDelegate createInstance = ObjectInstantiater(classType);

    // 执行该代理来获取指定对象的一个实例
    ISecurityProvider employeeInstance = createInstance() as ISecurityProvider;
}

如果你想知道的更多一些,可以参考以下代码(截取自 System.Reflection.Emit 命名空间)

// 实例方法的代理声明
public delegate object CreateInstanceDelegate();

// 缓存实例方法代理的 Dictionary
private static Dictionary<Type, CreateInstanceDelegate> _createInstanceDelegateList = 
	new Dictionary<Type, CreateInstanceDelegate>();

// 动态获取实例化指定类型的方法
public static CreateInstanceDelegate ObjectInstantiater(Type objectType)
{
    CreateInstanceDelegate createInstanceDelegate;

    if (!_createInstanceDelegateList.TryGetValue(objectType, 
        out createInstanceDelegate))
    {
        lock (objectType)
        {
            if (!_createInstanceDelegateList.TryGetValue(objectType, createInstanceDelegate))
            {
                // 建立一个新方法        
                DynamicMethod dynamicMethod =
                    new DynamicMethod("Create_" + objectType.Name, objectType, new Type[0]);

                // 获取默认的构造函数
                ConstructorInfo ctor = objectType.GetConstructor(new Type[0]);

                // 生成 IL 代码       
                ILGenerator ilgen = dynamicMethod.GetILGenerator();
                ilgen.Emit(OpCodes.Ldarg_0); // 构造函数有参数时需要
                ilgen.Emit(OpCodes.Newobj, ctor);
                ilgen.Emit(OpCodes.Ret);
                // 建立一个代理并缓存在 dictionary 中
                createInstanceDelegate = (CreateInstanceDelegate)dynamicMethod
                    .CreateDelegate(typeof(CreateInstanceDelegate));
                _createInstanceDelegateList[objectType] = createInstanceDelegate;
            }
        }
    }
    return createInstanceDelegate; // 返回对象的实例化代理
}

优点

  1. 类对象都通过静态方法建立
  2. 这个技术避免了用反射来进行类实例化

场景4(动态设定 ORM 实体对象的属性值)

绝大多数的 ORM Frameworks 中,实体类对应了数据库中的表,并通过反射来读取/设定实体类的各个属性,这非常影响性能。这可以借鉴场景3的方法该改善,此处我们将使用.NET Frameworks 3.5 中包含的 Expression Trees 特性来实现。

标准代码

// Employees 实体类
public class Employees
{
    public int EmployeeID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public DateTime BirthDate { get; set; }
}

// Employees 列表,用来存放从 data reader 读取到的所有数据
List<Employees> empList = new List<Employees>(); 

using(SqlConnection con = new SqlConnection(@"......"))
{
    SqlCommand cmd = new SqlCommand("Select * from employees");
    cmd.Connection = con;
    con.Open();
    // 调用 ReadList 把 data reader 查询出的信息存入实体类
    using (SqlDataReader reader = cmd.ExecuteReader())
    {
        empList = ReadList<Employees>(reader);
    }
}
// 把 data reader 查询出的信息转换成实体列表
public List<T> ReadList<T>(SqlDataReader reader) where T : new()
{
    var list = new List<T>();

    while (reader.Read())
    {
        T entity = new T();
        Type entityType = typeof(T);
        foreach(var entityProperty in entityType.GetProperties())
        {
            // 使用反射来设定实体对象的每一个属性
            entityProperty.SetValue(entity, reader[entityProperty.Name], null);
        }        list.Add(entity);
    }
    return list;
}

缺点

  1. 通过反射来设定属性值是个“重活”
  2. 开发者需要自行确保每个属性的类型安全

优化代码

public List<T> ReadList<T>(SqlDataReader reader)
{
    var list = new List<T>();  
    Func<SqlDataReader, T> readRow = GetReader<T>(); 

    // 从 GetReader 函数获取一个代理,并通过该代理来创建对象
    while (reader.Read())
    {
        list.Add(readRow(reader));
    }
    return list;
}

// 使用一个 ConcurrentDictionary 来缓存各类型的代理
ConcurrentDictionary<Type, Delegate> ExpressionCache = new ConcurrentDictionary<Type, Delegate>();

// 获取(并创建缓存)各类型的初始化 Expression Trees 
// PS:原文未提供该函数,所以翻译的时候自己写了下(简单调试通过),如有问题请联系我
public Func<SqlDataReader, T> GetReader<T>()
{
    Delegate func;
    var entityType = typeof(T);

    if (!ExpressionCache.TryGetValue(entityType, out func))
    {
        // lambda 输入参数
        var readerParam = Expression.Parameter(typeof(SqlDataReader), "reader");

        // lambda 处理
        var assignmentExpressions = new List<MemberBinding>();

        foreach (var entityProperty in entityType.GetProperties())
        {
            var assignmentExpression = Expression.Bind(
                entityProperty.GetSetMethod(),
                Expression.Convert(
                    Expression.Call(
                        readerParam,
                        typeof(SqlDataReader).GetMethod("get_Item", new[] { typeof(string) }),
                        Expression.Constant(entityProperty.Name, typeof(string))),
                    entityProperty.PropertyType));

            assignmentExpressions.Add(assignmentExpression);
        }

        var bodyExporess = Expression.MemberInit(Expression.New(entityType), assignmentExpressions);
        var lambda = Expression.Lambda<Func<SqlDataReader, T>>(bodyExporess, new[] { readerParam });
        func = lambda.Compile();
        ExpressionCache.TryAdd(entityType, func);
    }
    return (Func<SqlDataReader, T>)func;
}

优点

  1. 开发者不需要为实体类写赋值代码
  2. 建立的 expression tree 可被多个进程共享
  3. 建立 expression tree 相比写 IL 代码来说更简单

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值