zoukankan      html  css  js  c++  java
  • 反射,Expression Tree,IL Emit 属性操作对比

    • .net的反射(Reflection) 是.Net中获取运行时类型信息的一种方法,通过反射编码的方式可以获得 程序集,模块,类型,元数据等信息。
      反射的优点在于微软提供的API调用简单,使用方便;

    • 表达式树(Expression Tree)表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,表达式树经过编译后生成的直接是IL代码;

    • IL Emit 是直接操作IL的执行过程,对IL代码精细化控制;

    属性赋值操作 PropertyInfo.GetValue/SetValue是反射中常用的功能;
    三种实现方式的性能对比:

      public class Bar
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    
        public class Foo
        {
            public Bar Bar { get; set; }
    
            public static void SetReflection(Foo foo, Bar bar)
            {
                var property = foo.GetType().GetProperty("Bar");
                property.SetValue(foo, bar);
            }
    
            public static Action<Foo, Bar> SetExpression()
            {
                var property = typeof(Foo).GetProperty("Bar");
                var target = Expression.Parameter(typeof(Foo));
                ParameterExpression propertyValue = Expression.Parameter(typeof(Bar));
                //var setPropertyValue = Expression.Call(target, property.GetSetMethod(), propertyValue);
                BinaryExpression setPropertyValue = Expression.Assign(Expression.Property(target, property), propertyValue);
                var setAction = Expression.Lambda<Action<Foo, Bar>>(setPropertyValue, target, propertyValue).Compile();
                return setAction;
            }
    
            public static Action<Foo, Bar> SetEmit()
            {
                var property = typeof(Foo).GetProperty("Bar");
                DynamicMethod method = new DynamicMethod("SetValue", null, new Type[] { typeof(Foo), typeof(Bar) });
                ILGenerator ilGenerator = method.GetILGenerator();
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldarg_1);
                ilGenerator.EmitCall(OpCodes.Callvirt, property.GetSetMethod(), null);
                ilGenerator.Emit(OpCodes.Ret);
                method.DefineParameter(1, ParameterAttributes.In, "obj");
                method.DefineParameter(2, ParameterAttributes.In, "value");
                var setAction2 = (Action<Foo, Bar>)method.CreateDelegate(typeof(Action<Foo, Bar>));
                return setAction2;
            }
    
            public static void Test()
            {
                var act1 = Foo.SetExpression();
                var act2 = Foo.SetEmit();
    
                var st = new Stopwatch();
                st.Start();
    
                for (int i = 0; i < 1000000; i++)
                {
                    var foo = new Foo { };
                    var bar = new Bar { Id = 1, Name = "name" };
                    Foo.SetReflection(foo, bar);
                }
    
                Console.WriteLine("Reflection " + st.ElapsedMilliseconds);
    
                st.Restart();
                for (int i = 0; i < 1000000; i++)
                {
                    var foo = new Foo { };
                    var bar = new Bar { Id = 1, Name = "name" };
                    act1(foo, bar);
                }
    
                Console.WriteLine("Expression " + st.ElapsedMilliseconds);
    
                st.Restart();
                for (int i = 0; i < 1000000; i++)
                {
                    var foo = new Foo { };
                    var bar = new Bar { Id = 1, Name = "name" };
                    act2(foo, bar);
                }
    
                Console.WriteLine("Emit " + st.ElapsedMilliseconds);
            }
        }
    

    循环多次操作性能对比还是明显的表达式数和emit的性能优势明显;
    测试结果

    但是只循环一次的话 三种实现方式性能是无差别的,所以在一般情况下,反射的性能损失是可以忽略的;

  • 相关阅读:
    Linux中Shell的算数运算符和位运算符用法笔记
    Linux中Shell的算数运算符和位运算符用法笔记
    Linux中Shell的命令替换用法笔记
    Linux中Shell的命令替换用法笔记
    Linux中shell变量作用域笔记
    Linux中shell变量作用域笔记
    模块进阶、标准库、扩展库
    模块的导入
    私有化
    python作用域与LEGB规则
  • 原文地址:https://www.cnblogs.com/sands/p/11373569.html
Copyright © 2011-2022 走看看