ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Emit优化反射(属性的设置与获取)

2021-06-29 19:32:45  阅读:209  来源: 互联网

标签:反射 Console TotalMilliseconds Elapsed il WriteLine property Emit 属性


在频繁的通过反射来设置和获取属性的值时是比较耗时的,本章通过Emit技术优化反射来提高获取和设置属性值的效率

一、实现代码:

    /// <summary>
    /// 设置器委托
    /// </summary>
    /// <param name="target"></param>
    /// <param name="arg"></param>
    public delegate void SetValueDelegate(object target, object arg);
    /// <summary>
    /// 访问器委托
    /// </summary>
    /// <param name="target">目标对象</param>
    /// <returns></returns>
    public delegate object GetValueDelegate(object target);

    public static class DynamicMethodFactory
    {
        /// <summary>
        /// 获取访问器
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        public static GetValueDelegate GetGetter(this PropertyInfo property)
        {
            return DynamicMethodFactory.CreatePropertyGetter(property);
        }
        /// <summary>
        /// 获取设置器
        /// </summary>
        /// <param name="property"></param>
        /// <returns></returns>
        public static SetValueDelegate GetSetter(this PropertyInfo property)
        {
            return DynamicMethodFactory.CreatePropertySetter(property);
        }

        private static SetValueDelegate CreatePropertySetter(PropertyInfo property)
        {
            if (property == null)
            {
                throw new ArgumentNullException("property");
            }

            if (!property.CanWrite)
            {
                return null;
            }

            MethodInfo setMethod = property.GetSetMethod(true);

            DynamicMethod dm = new DynamicMethod("PropertySetter", null,
                new Type[] { typeof(object), typeof(object) }, property.DeclaringType, true);

            ILGenerator il = dm.GetILGenerator();

            if (!setMethod.IsStatic)
            {
                il.Emit(OpCodes.Ldarg_0);
            }
            il.Emit(OpCodes.Ldarg_1);

            EmitCastToReference(il, property.PropertyType);
            if (!setMethod.IsStatic && !property.DeclaringType.IsValueType)
            {
                il.EmitCall(OpCodes.Callvirt, setMethod, null);
            }
            else
            {
                il.EmitCall(OpCodes.Call, setMethod, null);
            }

            il.Emit(OpCodes.Ret);

            return (SetValueDelegate)dm.CreateDelegate(typeof(SetValueDelegate));
        }
        private static GetValueDelegate CreatePropertyGetter(PropertyInfo property)
        {
            if (property == null)
            {
                throw new ArgumentNullException("property");
            }

            if (!property.CanRead)
            {
                return null;
            }

            MethodInfo getMethod = property.GetGetMethod(true);

            DynamicMethod dm = new DynamicMethod("PropertyGetter", typeof(object), new[] { typeof(object) }, property.DeclaringType, true);

            Type returnType = getMethod.ReturnType;
            ILGenerator il = dm.GetILGenerator();


            il.Emit(OpCodes.Ldarg_0);
            EmitCastToReference(il, getMethod.DeclaringType);

            if (getMethod.IsFinal)
            {
                il.Emit(OpCodes.Call, getMethod);
            }
            else
            {
                il.Emit(OpCodes.Callvirt, getMethod);
            }

            if (returnType.IsValueType)
            {
                il.Emit(OpCodes.Box, returnType);
            }

            il.Emit(OpCodes.Ret);
            il.Emit(OpCodes.Ret);

            return (GetValueDelegate)dm.CreateDelegate(typeof(GetValueDelegate));
        }
        private static void EmitCastToReference(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, type);
            }
            else
            {
                il.Emit(OpCodes.Castclass, type);
            }
        }
    }

二、测试代码:

#define SETTER
//#define GETTER

using System;
using System.Diagnostics;
using System.Reflection;

namespace PublishConsoleApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            // 输出当前运行时
            Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion());

            // 通过最顶端的预编译符号(SETTER/GETTER)控制测试的代码块

            // 调用100w次时间比较
            int count = 100_0000;
            // 测试次数
            int testTimes = 5;
            OrderInfo testObj = new OrderInfo() { OrderID = 123 };
            PropertyInfo propInfo = typeof(OrderInfo).GetProperty("OrderID");

            for (int k = 0; k < testTimes; k++)
            {
#if SETTER
                Stopwatch watch1 = Stopwatch.StartNew();

                Console.WriteLine($"------------- 设置器测试 ------------- ");
                for (int i = 0; i < count; i++)
                {
                    testObj.OrderID = 1;
                }

                watch1.Stop();
                Console.WriteLine($"直接设置花费时间:       {watch1.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////

                var setter = propInfo.GetSetter();
                Stopwatch watch2 = Stopwatch.StartNew();

                for (int i = 0; i < count; i++)
                {
                    setter(testObj, 2);
                }

                watch2.Stop();
                Console.WriteLine($"EmitSet设置花费时间:        {watch2.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////

                Stopwatch watch3 = Stopwatch.StartNew();

                for (int i = 0; i < count; i++)
                {
                    propInfo.SetValue(testObj, 3, null);
                }

                watch3.Stop();
                Console.WriteLine($"纯反射设置花费时间:        {watch3.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////

                Console.WriteLine("-------------------");
                // 设置器
                Console.WriteLine("纯反射/直接设置:{0} / {1} = {2}",
                    watch3.Elapsed.TotalMilliseconds.ToString(),
                    watch1.Elapsed.TotalMilliseconds.ToString(),
                    watch3.Elapsed.TotalMilliseconds / watch1.Elapsed.TotalMilliseconds);

                Console.WriteLine("纯反射/EmitSet:{0} / {1} = {2}",
                    watch3.Elapsed.TotalMilliseconds.ToString(),
                    watch2.Elapsed.TotalMilliseconds.ToString(),
                    watch3.Elapsed.TotalMilliseconds / watch2.Elapsed.TotalMilliseconds);

                Console.WriteLine("EmitSet/直接设置:{0} / {1} = {2}",
                    watch2.Elapsed.TotalMilliseconds.ToString(),
                    watch1.Elapsed.TotalMilliseconds.ToString(),
                    watch2.Elapsed.TotalMilliseconds / watch1.Elapsed.TotalMilliseconds);
#endif

#if GETTER

                Console.WriteLine($"------------- 访问器测试 ------------- ");
                Stopwatch watch4 = Stopwatch.StartNew();

                for (int i = 0; i < count; i++)
                {
                    var orderId = testObj.OrderID;
                }

                watch4.Stop();
                Console.WriteLine($"直接访问花费时间:        {watch4.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////
                testObj.OrderID = 4;
                var getter = propInfo.GetGetter();
                Stopwatch watch5 = Stopwatch.StartNew();

                for (int i = 0; i < count; i++)
                {
                    var orderId = getter(testObj);
                }

                watch5.Stop();
                Console.WriteLine($"EmitSet访问花费时间:        {watch5.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////

                testObj.OrderID = 5;
                Stopwatch watch6 = Stopwatch.StartNew();

                for (int i = 0; i < count; i++)
                {
                    var orderId = propInfo.GetValue(testObj);
                }

                watch6.Stop();
                Console.WriteLine($"纯反射访问花费时间:        {watch6.Elapsed.TotalMilliseconds} 毫秒");

                ////////////////////////////////////////////////////

                Console.WriteLine("-------------------");

                // 访问器
                Console.WriteLine("纯反射/直接设置:{0} / {1} = {2}",
                    watch6.Elapsed.TotalMilliseconds.ToString(),
                    watch4.Elapsed.TotalMilliseconds.ToString(),
                    watch6.Elapsed.TotalMilliseconds / watch4.Elapsed.TotalMilliseconds);

                Console.WriteLine("纯反射/EmitSet:{0} / {1} = {2}",
                    watch6.Elapsed.TotalMilliseconds.ToString(),
                    watch5.Elapsed.TotalMilliseconds.ToString(),
                    watch6.Elapsed.TotalMilliseconds / watch5.Elapsed.TotalMilliseconds);

                Console.WriteLine("EmitSet/直接设置:{0} / {1} = {2}",
                    watch5.Elapsed.TotalMilliseconds.ToString(),
                    watch4.Elapsed.TotalMilliseconds.ToString(),
                    watch5.Elapsed.TotalMilliseconds / watch4.Elapsed.TotalMilliseconds);
#endif
            }

            //Console.WriteLine("Hello World!");
            Console.ReadKey();
        }
    }


    public class OrderInfo
    {
        public int OrderID { get; set; }
    }
}

 

测试结果:

 

 

标签:反射,Console,TotalMilliseconds,Elapsed,il,WriteLine,property,Emit,属性
来源: https://www.cnblogs.com/lanwah/p/14951626.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有