日期:2014-05-20  浏览次数:21019 次

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