读书人

Emit 怎么对一个私有属性进行赋值

发布时间: 2012-03-07 09:13:51 作者: rapoo

Emit 如何对一个私有属性进行赋值?

C# code
public class FastProperty<T>    {        public delegate void SetValueDelegateHandler(T owner, object value);        private readonly Type ParameterType = typeof(object);        private T _owner;        public T Owner { get { return this._owner; } }        private Type _ownerType;        public FastProperty(T owner)        {            this._owner = owner;            this._ownerType = typeof(T);        }        public SetValueDelegateHandler SetPropertyValue(string propertyName, object value)        {            // 指定函数名            string methodName = "set_" + propertyName;            // 搜索函数,不区分大小写 IgnoreCase            var callMethod = this._ownerType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);            // 获取参数            var para = callMethod.GetParameters()[0];            // 创建动态函数            DynamicMethod method = new DynamicMethod("EmitCallable", null, new Type[] { this._ownerType, ParameterType }, this._ownerType.Module);            // 获取动态函数的 IL 生成器            var il = method.GetILGenerator();            // 创建一个本地变量,主要用于 Object Type to Propety Type            var local = il.DeclareLocal(para.ParameterType, true);            // 加载第 2 个参数【(T owner, object value)】的 value            il.Emit(OpCodes.Ldarg_1);            if (para.ParameterType.IsValueType)            {                il.Emit(OpCodes.Unbox_Any, para.ParameterType);// 如果是值类型,拆箱 string = (string)object;            }            else            {                il.Emit(OpCodes.Castclass, para.ParameterType);// 如果是引用类型,转换 Class = object as Class            }            il.Emit(OpCodes.Stloc, local);// 将上面的拆箱或转换,赋值到本地变量,现在这个本地变量是一个与目标函数相同数据类型的字段了。            il.Emit(OpCodes.Ldarg_0);   // 加载第一个参数 owner            il.Emit(OpCodes.Ldloc, local);// 加载本地参数            il.EmitCall(OpCodes.Callvirt, callMethod, null);//调用函数            il.Emit(OpCodes.Ret);   // 返回            /* 生成的动态函数类似:             * void EmitCallable(T owner, object value)             * {             *     T local = (T)value;             *     owner.Method(local);             * }             */            return method.CreateDelegate(typeof(SetValueDelegateHandler)) as SetValueDelegateHandler;        }    }


详情:
C# code
http://www.cnblogs.com/mrlen/archive/2010/06/10/1755357.html

这个方法可以访问非私有的函数或属性。
可是对于私有属性就无能为力了。

我这里有 3.5 才有 LINQ 方式。可是我希望是基于 2.0 实现的。不知道上面有哪个地方需要“改”?

好久没有发200分的帖子了……可用分一直很少。都不敢发问。~~~~

[解决办法]
不需要emit,直接用反射+委托做,性能不比emit差:
C# code
class T { private object P { get; set; } }//得到私有属性System.Reflection.PropertyInfo p = typeof(T).GetProperty("P", ystem.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);//创建get,set委托T t = new T();Func<object> PGet = Delegate.CreateDelegate(typeof(Func<object>), t, p.GetGetMethod(true)) as Func<object>;Action<object> PSet = Delegate.CreateDelegate(typeof(Action<object>), t, p.GetSetMethod(true)) as Action<object>;//访问属性PSet(new object());object o = PGet();
[解决办法]
多谢gomoku和guoyichao的指点。
如gomoku所说,那样测试的确有点不合适。
多谢guoyichao的提醒,这样的确很快。
重新修改了测试例子:
C# code
using System;using System.Reflection;using System.Reflection.Emit;class Program{    static void Main(string[] args)    {        //声明自定义类型,实例化        TestClass obj = new TestClass();        //调用静态方法设置值        FastProperty<TestClass> fp = new FastProperty<TestClass>(obj);        FastPropertyBufferd<TestClass> fp2 = new FastPropertyBufferd<TestClass>(obj, "Value");        int tick = Environment.TickCount;        FastProperty<TestClass>.SetValueDelegateHandler setter = fp.SetPropertyValue("Value", 5000);        for (int i = 0; i < 1000000; i++)        {            setter.Invoke(obj, 5000);        }        tick = Environment.TickCount - tick;        Console.WriteLine("FastProperty".PadRight(20) + " : " + tick.ToString());        tick = Environment.TickCount;        for (int i = 0; i < 1000000; i++)        {            fp2.SetValue(5000);        }        tick = Environment.TickCount - tick;        Console.WriteLine("FastPropertyBufferd".PadRight(20) + " : " + tick);        tick = Environment.TickCount;        for (int i = 0; i < 1000000; i++)        {            SetNonePublicPropertyValue(obj, "Value", 5000);        }        tick = Environment.TickCount - tick;        Console.WriteLine("Direct reflect".PadRight(20) + " : " + tick);        tick = Environment.TickCount;        SetValueHandler del = Delegate.CreateDelegate(typeof(SetValueHandler),obj, "set_Value") as SetValueHandler;        for (int i = 0; i < 1000000; i++)        {            del(5000);        }        tick = Environment.TickCount - tick;        Console.WriteLine("CreateDelegate reflect".PadRight(20) + " : " + tick);        Console.ReadKey();    }    delegate void SetValueHandler(Int32 value);    public class TestClass    {        private int _value = 500;        public int Value        {            get            {                return _value;            }            set            {                _value = value;                //Console.WriteLine("Value changed! New value is " + value.ToString());            }        }    }    /// <summary>    /// 设置Protected/Private类型的属性值    /// </summary>    /// <param name="Owner">要设置的对象</param>    /// <param name="propertyName">属性名</param>    /// <param name="value">值</param>    public static void SetNonePublicPropertyValue(object Owner, string propertyName, object value)    {        //Owner.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).ToList().ForEach(p => Console.WriteLine(p.Name));        //获取非公开的成员属性        PropertyInfo pi = Owner.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);        if (pi != null)//如果获取到了,说明你调用没错        {            pi.SetValue(Owner, value, null);//设置值,这个最后一个属性,是索引值,如果属性本身是Array或List,这里要写索引编号或索引器要求的值。        }    }    /// <summary>    /// 缓存反射结果的类,提高访问速度    /// </summary>    /// <typeparam name="T">类型</typeparam>    public class FastPropertyBufferd<T>    {        private T _owner = default(T);        MethodInfo mi = null;        public FastPropertyBufferd(T owner, string propertyName)        {            _owner = owner;            mi = typeof(T).GetMethod("set_" + propertyName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);        }        public void SetValue(object value)        {            mi.Invoke(_owner, new object[] { value });        }    }    public class FastProperty<T>    {        public delegate void SetValueDelegateHandler(T owner, object value);        private readonly Type ParameterType = typeof(object);        private T _owner;        public T Owner { get { return this._owner; } }        private Type _ownerType;        public FastProperty(T owner)        {            this._owner = owner;            this._ownerType = typeof(T);        }        public void SetPropertyValue2(string propertyName, object value)        {            PropertyInfo[] pis = Owner.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);            foreach (PropertyInfo pi in pis)            {                if (pi.Name == propertyName)                {                    pi.SetValue(Owner, value, null);                }            }        }        public SetValueDelegateHandler SetPropertyValue(string propertyName, object value)        {            // 指定函数名            string methodName = "set_" + propertyName;            // 搜索函数,不区分大小写 IgnoreCase            MethodInfo callMethod = this._ownerType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);            // 获取参数            ParameterInfo para = callMethod.GetParameters()[0];            // 创建动态函数            DynamicMethod method = new DynamicMethod("EmitCallable", null, new Type[] { this._ownerType, ParameterType }, this._ownerType.Module);            // 获取动态函数的 IL 生成器            ILGenerator il = method.GetILGenerator();            // 创建一个本地变量,主要用于 Object Type to Propety Type            LocalBuilder local = il.DeclareLocal(para.ParameterType, true);            // 加载第 2 个参数【(T owner, object value)】的 value            il.Emit(OpCodes.Ldarg_1);            if (para.ParameterType.IsValueType)            {                il.Emit(OpCodes.Unbox_Any, para.ParameterType);// 如果是值类型,拆箱 string = (string)object;            }            else            {                il.Emit(OpCodes.Castclass, para.ParameterType);// 如果是引用类型,转换 Class = object as Class            }            il.Emit(OpCodes.Stloc, local);// 将上面的拆箱或转换,赋值到本地变量,现在这个本地变量是一个与目标函数相同数据类型的字段了。            il.Emit(OpCodes.Ldarg_0);   // 加载第一个参数 owner            il.Emit(OpCodes.Ldloc, local);// 加载本地参数            il.EmitCall(OpCodes.Callvirt, callMethod, null);//调用函数            il.Emit(OpCodes.Ret);   // 返回            /* 生成的动态函数类似:             * void EmitCallable(T owner, object value)             * {             *     T local = (T)value;             *     owner.Method(local);             * }             */            return method.CreateDelegate(typeof(SetValueDelegateHandler)) as SetValueDelegateHandler;        }    }} 


[解决办法]
// 创建动态函数
DynamicMethod method = new DynamicMethod("EmitCallable", null, new Type[] { this._ownerType, ParameterType }, this._ownerType.Module, true);
加上的true意味着跳过检查(skipVisibility),然后私有/非私有就都可以访问了。

另:23楼guoyichao的方法有个局限,Func<object>不能推广到Func<T>的情况,除非显式指定属性的类型T。


读书人网 >C#

热点推荐