高性能反射库 ReflectionHelper

地址:http://www.codeproject.com/Articles/412968/ReflectionHelper

主文件:

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using Pfz.Threading;
using Pfz.Extensions;
using Pfz.DataTypes;

namespace Pfz
{
    /// <summary>
    /// This class allows you to get members from types more safely than using
    /// string literals. It only exists because C# does not have fieldinfoof,
    /// propertyinfoof and methodinfoof.
    /// </summary>
    public static class ReflectionHelper
    {
        #region GetMember
        /// <summary>
        /// Gets a member by it's expression usage.
        /// For example, GetMember(() => obj.GetType()) will return the
        /// GetType method.
        /// </summary>
        public static MemberInfo GetMember<T>(Expression<Func<T>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var body = expression.Body;

            switch (body.NodeType)
            {
                case ExpressionType.MemberAccess:
                    MemberExpression memberExpression = (MemberExpression)body;
                    return memberExpression.Member;

                case ExpressionType.Call:
                    MethodCallExpression callExpression = (MethodCallExpression)body;
                    return callExpression.Method;

                case ExpressionType.New:
                    NewExpression newExpression = (NewExpression)body;
                    return newExpression.Constructor;
            }

            throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
        }
        #endregion
        #region GetConstructor
        /// <summary>
        /// Gets the constructor info from a sample construction call expression.
        /// Example: GetConstructor(() => new Control()) will return the constructor
        /// info for the default constructor of Control.
        /// </summary>
        public static ConstructorInfo GetConstructor<T>(Expression<Func<T>> expression)
        {
            return (ConstructorInfo)GetMember(expression);
        }
        #endregion
        #region GetField
        /// <summary>
        /// Gets a field from a sample usage.
        /// Example: GetField(() => Type.EmptyTypes) will return the FieldInfo of
        /// EmptyTypes.
        /// </summary>
        public static FieldInfo GetField<T>(Expression<Func<T>> expression)
        {
            return (FieldInfo)GetMember(expression);
        }
        #endregion
        #region GetProperty
        /// <summary>
        /// Gets a property from a sample usage.
        /// Example: GetProperty(() => str.Length) will return the property info 
        /// of Length.
        /// </summary>
        public static PropertyInfo GetProperty<T>(Expression<Func<T>> expression)
        {
            return (PropertyInfo)GetMember(expression);
        }
        #endregion
        #region GetMethod
        /// <summary>
        /// Gets a method info of a void method.
        /// Example: GetMethod(() => Console.WriteLine("")); will return the
        /// MethodInfo of WriteLine that receives a single argument.
        /// </summary>
        public static MethodInfo GetMethod(Expression<Action> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var body = expression.Body;
            if (body.NodeType != ExpressionType.Call)
                throw new ArgumentException("expression.Body must be a Call expression.", "expression");

            MethodCallExpression callExpression = (MethodCallExpression)body;
            return callExpression.Method;
        }

        /// <summary>
        /// Gets the MethodInfo of a method that returns a value.
        /// Example: GetMethod(() => Console.ReadLine()); will return the method info
        /// of ReadLine.
        /// </summary>
        public static MethodInfo GetMethod<T>(Expression<Func<T>> expression)
        {
            return (MethodInfo)GetMember(expression);
        }
        #endregion

        #region GetDefaultConstructorDelegate
        private static YieldReaderWriterLockSlim _defaultConstructorsLock;
        private static readonly Dictionary<Type, Func<object>> _defaultConstructorsDictionary = new Dictionary<Type, Func<object>>();
        private static readonly Func<Type, Func<object>> _getDefaultConstructorDelegate = GetDefaultConstructorDelegate<object>;

        /// <summary>
        /// Gets a function that creates objects of the given type.
        /// The object must have a default constructor.
        /// </summary>
        public static Func<object> GetDefaultConstructorDelegate(Type objectType)
        {
            var result = _defaultConstructorsDictionary.GetOrCreateValue(ref _defaultConstructorsLock, objectType, _getDefaultConstructorDelegate);
            return result;
        }

        private static YieldReaderWriterLockSlim _typedDefaultConstructorsLock;
        private static readonly Dictionary<KeyValuePair<Type, Type>, Delegate> _typedDefaultConstructorsDictionary = new Dictionary<KeyValuePair<Type, Type>, Delegate>();
        private static readonly Func<KeyValuePair<Type, Type>, Delegate> _getTypedDefaultConstructorDelegate = _GetDefaultConstructorDelegate;
        /// <summary>
        /// Gets the default constructor for the given objectType, but return
        /// it already casted to a given "T".
        /// </summary>
        public static Func<T> GetDefaultConstructorDelegate<T>(Type objectType)
        {
            if (objectType == null)
                throw new ArgumentNullException("objectType");

            var pair = Pair.Create(typeof(Func<T>), objectType);
            var result = _typedDefaultConstructorsDictionary.GetOrCreateValue(ref _typedDefaultConstructorsLock, pair, _getTypedDefaultConstructorDelegate);
            return (Func<T>)result;
        }

        private static Delegate _GetDefaultConstructorDelegate(KeyValuePair<Type, Type> pair)
        {
            var funcType = pair.Key;
            var objectType = pair.Value;
            var resultType = funcType.GetGenericArguments()[0];

            Expression expression = Expression.New(objectType);

            if
            (
                resultType != objectType &&
                (
                    objectType.IsValueType ||
                    !resultType.IsAssignableFrom(objectType)
                )
            )
            {
                expression = Expression.Convert(expression, resultType);
            }

            var lambdaExpression = Expression.Lambda(funcType, expression);
            var func = lambdaExpression.Compile();

            return func;
        }
        #endregion
        #region GetConstructorDelegate<T>
        /// <summary>
        /// Creates a delegate (of type T) for the given constructor.
        /// The delegate type should match the number of parameters in the constructor.
        /// Casts are done if required but no other conversions are done.
        /// </summary>
        public static T GetConstructorDelegate<T>(ConstructorInfo constructor)
        {
            object result = GetConstructorDelegate(constructor, typeof(T));
            return (T)result;
        }

        private static YieldReaderWriterLockSlim _getTypedConstructorLock;
        private static readonly Dictionary<KeyValuePair<ConstructorInfo, Type>, Delegate> _typedConstructors = new Dictionary<KeyValuePair<ConstructorInfo, Type>, Delegate>();
        private static readonly Func<KeyValuePair<ConstructorInfo, Type>, Delegate> _getTypedConstructorDelegate = _GetConstructorDelegate;
        /// <summary>
        /// Creates a delegate for the given constructor.
        /// The delegate type should match the number of parameters in the constructor.
        /// Casts are done if required but no other conversions are done.
        /// </summary>
        public static Delegate GetConstructorDelegate(ConstructorInfo constructor, Type delegateType)
        {
            if (constructor == null)
                throw new ArgumentNullException("constructor");

            if (!delegateType.IsSubclassOf(typeof(Delegate)))
                throw new ArgumentException("delegateType is not a Delegate.", "delegateType");

            var pair = Pair.Create(constructor, delegateType);
            var result = _typedConstructors.GetOrCreateValue(ref _getTypedConstructorLock, pair, _getTypedConstructorDelegate);
            return result;
        }

        private static Delegate _GetConstructorDelegate(KeyValuePair<ConstructorInfo, Type> pair)
        {
            var constructor = pair.Key;
            var delegateType = pair.Value;

            var invokeMethod = delegateType.GetMethod("Invoke");
            if (invokeMethod == null)
                throw new InvalidOperationException("The given delegate type does not have an Invoke method. Is this a compilation error?");

            var constructorType = constructor.DeclaringType;
            var invokeReturnType = invokeMethod.ReturnType;

            bool isInvokeVoid = invokeReturnType == typeof(void);
            if (isInvokeVoid)
                throw new InvalidOperationException("The return type the delegate is incompatible.");

            var invokeParameterTypes = invokeMethod.GetParameterTypes();
            var constructorParameterTypes = constructor.GetParameterTypes();

            int count = invokeParameterTypes.Length;
            if (constructorParameterTypes.Length != count)
                throw new InvalidOperationException("The number of parameters between the constructor and the delegate is not compatible.");

            var parameterExpressions = new ParameterExpression[count];
            var arguments = new Expression[count];
            for (int i = 0; i < count; i++)
            {
                var argument = _GetArgumentExpression(i, constructorParameterTypes, invokeParameterTypes, parameterExpressions);
                arguments[i] = argument;
            }

            Expression resultExpression = Expression.New(constructor, arguments);

            if (constructorType != invokeReturnType)
                resultExpression = Expression.Convert(resultExpression, invokeReturnType);

            var lambda = Expression.Lambda(delegateType, resultExpression, parameterExpressions);
            var compiled = lambda.Compile();
            return compiled;
        }
        #endregion

#if !WINDOWS_PHONE
        #region GetConstructorDelegate
        private static YieldReaderWriterLockSlim _constructorsLock;
        private static readonly Dictionary<ConstructorInfo, FastDynamicDelegate> _constructors = new Dictionary<ConstructorInfo, FastDynamicDelegate>();
        private static readonly Func<ConstructorInfo, FastDynamicDelegate> _getConstructorDelegate = _CreateConstructorDelegate;
        /// <summary>
        /// Gets a constructor invoker delegate for the given constructor info.
        /// Using the delegate is much faster than calling the Invoke method on the constructor,
        /// but if you invoke it only once, it will do no good as some time is spent compiling
        /// such delegate.
        /// </summary>
        public static FastDynamicDelegate GetConstructorDelegate(ConstructorInfo constructor)
        {
            if (constructor == null)
                throw new ArgumentNullException("constructor");

            var result = _constructors.GetOrCreateValue(ref _constructorsLock, constructor, _getConstructorDelegate);
            return result;
        }


        private static FastDynamicDelegate _CreateConstructorDelegate(ConstructorInfo constructor)
        {
            var parametersExpression = Expression.Parameter(typeof(object[]), "parameters");

            var variables = new List<ParameterExpression>();
            var beforeInstructions = new List<Expression>();
            var afterInstructions = new List<Expression>();

            Expression[] arrayAccesses = null;
            var parameters = constructor.GetParameters();
            int count = parameters.Length;
            if (count != 0)
            {
                arrayAccesses = new Expression[count];
                for (int i = 0; i < count; i++)
                {
                    var parameter = parameters[i];
                    var parameterType = parameter.ParameterType;

                    var constantExpression = Expression.Constant(i);
                    Expression accessParameterExpression = Expression.ArrayAccess(parametersExpression, constantExpression);

                    if (parameterType.IsByRef)
                    {
                        parameterType = parameterType.GetElementType();

                        if (parameterType != typeof(object))
                        {
                            var variable = Expression.Variable(parameterType);
                            variables.Add(variable);
                            arrayAccesses[i] = variable;

                            if (!parameter.IsOut)
                            {
                                var effectiveAccessParameterExpression = accessParameterExpression;
                                if (parameterType != typeof(object))
                                    effectiveAccessParameterExpression = Expression.Convert(accessParameterExpression, parameterType);

                                var setIn = Expression.Assign(variable, effectiveAccessParameterExpression);
                                beforeInstructions.Add(setIn);
                            }

                            Expression accessVariable = variable;
                            if (parameterType != typeof(object))
                                accessVariable = Expression.Convert(variable, typeof(object));

                            var setOut = Expression.Assign(accessParameterExpression, accessVariable);
                            afterInstructions.Add(setOut);
                            continue;
                        }
                    }

                    if (parameterType != typeof(object))
                        accessParameterExpression = Expression.Convert(accessParameterExpression, parameterType);

                    arrayAccesses[i] = accessParameterExpression;
                }
            }

            var newExpression = Expression.New(constructor, arrayAccesses);
            var returnTarget = Expression.Label(typeof(object));
            var instructions = new List<Expression>();
            instructions.AddRange(beforeInstructions);

            ParameterExpression resultVariable = null;
            Expression body = newExpression;
            if (constructor.DeclaringType != typeof(object))
                body = Expression.Convert(newExpression, typeof(object));

            resultVariable = Expression.Variable(typeof(object));
            variables.Add(resultVariable);
            body = Expression.Assign(resultVariable, body);
            instructions.Add(body);

            instructions.AddRange(afterInstructions);
            var returnExpression = Expression.Return(returnTarget, resultVariable);
            instructions.Add(returnExpression);

            instructions.Add(Expression.Label(returnTarget, Expression.Constant(null, typeof(object))));
            body = Expression.Block(typeof(object), variables, instructions);

            var result = Expression.Lambda<FastDynamicDelegate>(body, parametersExpression);
            return result.Compile();
        }
        #endregion
        #region GetMethodCallDelegate
        private static YieldReaderWriterLockSlim _methodsLock;
        private static readonly Dictionary<MethodInfo, FastMethodCallDelegate> _methodsDictionary = new Dictionary<MethodInfo, FastMethodCallDelegate>();
        /// <summary>
        /// Gets a delegate to call the given method in a fast manner.
        /// </summary>
        public static FastMethodCallDelegate GetMethodCallDelegate(MethodInfo method)
        {
            var result = _methodsDictionary.GetOrCreateValue(ref _methodsLock, method, _CreateMethodCallDelegate);
            return result;
        }
        private static FastMethodCallDelegate _CreateMethodCallDelegate(MethodInfo method)
        {
            var parametersExpression = Expression.Parameter(typeof(object[]));

            ParameterExpression targetExpression = Expression.Parameter(typeof(object));

            Expression castTarget = null;

            if (!method.IsStatic)
            {
                castTarget = targetExpression;
                if (method.ReturnType != typeof(object))
                    castTarget = Expression.Convert(targetExpression, method.DeclaringType);
            }

            var variables = new List<ParameterExpression>();
            var beforeInstructions = new List<Expression>();
            var afterInstructions = new List<Expression>();

            Expression[] arrayAccesses = null;
            var parameters = method.GetParameters();
            int count = parameters.Length;
            if (count != 0)
            {
                arrayAccesses = new Expression[count];
                for (int i = 0; i < count; i++)
                {
                    var parameter = parameters[i];
                    var parameterType = parameter.ParameterType;

                    var constantExpression = Expression.Constant(i);
                    Expression accessParameterExpression = Expression.ArrayAccess(parametersExpression, constantExpression);

                    if (parameterType.IsByRef)
                    {
                        parameterType = parameterType.GetElementType();

                        if (parameterType != typeof(object))
                        {
                            var variable = Expression.Variable(parameterType);
                            variables.Add(variable);
                            arrayAccesses[i] = variable;

                            if (!parameter.IsOut)
                            {
                                var effectiveAccessParameterExpression = accessParameterExpression;
                                if (parameterType != typeof(object))
                                    effectiveAccessParameterExpression = Expression.Convert(accessParameterExpression, parameterType);

                                var setIn = Expression.Assign(variable, effectiveAccessParameterExpression);
                                beforeInstructions.Add(setIn);
                            }

                            Expression accessVariable = variable;
                            if (parameterType != typeof(object))
                                accessVariable = Expression.Convert(variable, typeof(object));

                            var setOut = Expression.Assign(accessParameterExpression, accessVariable);
                            afterInstructions.Add(setOut);
                            continue;
                        }
                    }

                    if (parameterType != typeof(object))
                        accessParameterExpression = Expression.Convert(accessParameterExpression, parameterType);

                    arrayAccesses[i] = accessParameterExpression;
                }
            }

            MethodCallExpression callExpression;

            if (method.IsStatic)
                callExpression = Expression.Call(method, arrayAccesses);
            else
                callExpression = Expression.Call(castTarget, method, arrayAccesses);

            var returnTarget = Expression.Label(typeof(object));
            var instructions = new List<Expression>();
            instructions.AddRange(beforeInstructions);

            ParameterExpression resultVariable = null;
            Expression body = callExpression;
            if (method.ReturnType != typeof(void))
            {
                if (method.ReturnType != typeof(object))
                    body = Expression.Convert(callExpression, typeof(object));

                resultVariable = Expression.Variable(typeof(object));
                variables.Add(resultVariable);
                body = Expression.Assign(resultVariable, body);
            }
            instructions.Add(body);

            instructions.AddRange(afterInstructions);

            if (method.ReturnType == typeof(void))
            {
                var returnExpression = Expression.Return(returnTarget, Expression.Constant(null, typeof(object)), typeof(object));
                instructions.Add(returnExpression);
            }
            else
            {
                var returnExpression = Expression.Return(returnTarget, resultVariable);
                instructions.Add(returnExpression);
            }

            instructions.Add(Expression.Label(returnTarget, Expression.Constant(null, typeof(object))));
            body = Expression.Block(typeof(object), variables, instructions);

            var result = Expression.Lambda<FastMethodCallDelegate>(body, targetExpression, parametersExpression);

            return result.Compile();
        }
        #endregion
#endif


        #region GetMethodCallDelegate<T>
        /// <summary>
        /// Creates a method call delegate for the given method info.
        /// The delegateType (T) should have the same amount of parameters as the method. Note
        /// that non-static methods have a first parameter to represent the instance.
        /// </summary>
        public static T GetMethodCallDelegate<T>(MethodInfo method)
        {
            object result = GetMethodCallDelegate(method, typeof(T));
            return (T)result;
        }

        private static YieldReaderWriterLockSlim _getTypedMethodCallLock;
        private static readonly Dictionary<KeyValuePair<MethodInfo, Type>, Delegate> _getTypedMethodCallDictionary = new Dictionary<KeyValuePair<MethodInfo, Type>, Delegate>();
        private static readonly Func<KeyValuePair<MethodInfo, Type>, Delegate> _getTypedMethodCallDelegate = _GetTypedMethodCallDelegate;
        /// <summary>
        /// Creates a method call delegate for the given method info.
        /// The delegateType should have the same amount of parameters as the method. Note
        /// that non-static methods have a first parameter to represent the instance.
        /// </summary>
        public static Delegate GetMethodCallDelegate(MethodInfo method, Type delegateType)
        {
            if (method == null)
                throw new ArgumentNullException("method");

            if (!delegateType.IsSubclassOf(typeof(Delegate)))
                throw new ArgumentException("delegateType is not a Delegate.", "delegateType");

            var pair = Pair.Create(method, delegateType);
            var result = _getTypedMethodCallDictionary.GetOrCreateValue(ref _getTypedMethodCallLock, pair, _getTypedMethodCallDelegate);
            return result;
        }
        private static Delegate _GetTypedMethodCallDelegate(KeyValuePair<MethodInfo, Type> pair)
        {
            var method = pair.Key;
            var delegateType = pair.Value;

            var invokeMethod = delegateType.GetMethod("Invoke");
            if (invokeMethod == null)
                throw new InvalidOperationException("The given delegate type does not have an Invoke method. Is this a compilation error?");

            var methodReturnType = method.ReturnType;
            var invokeReturnType = invokeMethod.ReturnType;

            bool isMethodVoid = methodReturnType == typeof(void);
            bool isInvokeVoid = invokeReturnType == typeof(void);
            if (isMethodVoid != isInvokeVoid)
                throw new InvalidOperationException("The return type of the method and the delegate is incompatible.");

            var invokeParameterTypes = invokeMethod.GetParameterTypes();
            var methodParameterTypes = new List<Type>();
            if (!method.IsStatic)
                methodParameterTypes.Add(method.DeclaringType);

            methodParameterTypes.AddRange(method.GetParameterTypes());

            int count = invokeParameterTypes.Length;
            if (methodParameterTypes.Count != count)
                throw new InvalidOperationException("The number of parameters between the method and the delegate is not compatible. Note that non-static methods have the additional \"this\" parameter as the first one.");

            var parameterExpressions = new ParameterExpression[count];

            int startIndex = 0;
            int argumentCount = count;
            if (!method.IsStatic)
            {
                startIndex = 1;
                argumentCount--;
            }

            var arguments = new Expression[argumentCount];
            for (int i = 0; i < argumentCount; i++)
            {
                var argument = _GetArgumentExpression(i + startIndex, methodParameterTypes, invokeParameterTypes, parameterExpressions);
                arguments[i] = argument;
            }

            MethodCallExpression callExpression;
            if (method.IsStatic)
                callExpression = Expression.Call(method, arguments);
            else
            {
                var instanceExpression = _GetArgumentExpression(0, methodParameterTypes, invokeParameterTypes, parameterExpressions);
                callExpression = Expression.Call(instanceExpression, method, arguments);
            }

            Expression resultExpression = callExpression;
            if (methodReturnType != invokeReturnType)
                resultExpression = Expression.Convert(resultExpression, invokeReturnType);

            var lambda = Expression.Lambda(delegateType, resultExpression, parameterExpressions);
            var compiled = lambda.Compile();
            return compiled;
        }
        private static Expression _GetArgumentExpression(int index, IList<Type> methodParameterTypes, Type[] invokeParameterTypes, ParameterExpression[] parameterExpressions)
        {
            var invokeParameterType = invokeParameterTypes[index];
            var methodParameterType = methodParameterTypes[index];

            var parameter = Expression.Parameter(invokeParameterType, "P" + index);
            parameterExpressions[index] = parameter;
            if (methodParameterType == invokeParameterType)
                return parameter;

            var convert = Expression.Convert(parameter, methodParameterType);
            return convert;
        }
        #endregion

        #region GetPropertyGetterDelegate
        private static YieldReaderWriterLockSlim _getPropertyLock;
        private static readonly Dictionary<PropertyInfo, Func<object, object>> _getPropertiesDictionary = new Dictionary<PropertyInfo, Func<object, object>>();
        private static readonly Func<PropertyInfo, Func<object, object>> _getPropertyGetterDelegate = GetPropertyGetterDelegate<object, object>;
        /// <summary>
        /// Gets a delegate to read values from the given property in a very fast manner.
        /// </summary>
        public static Func<object, object> GetPropertyGetterDelegate(PropertyInfo property)
        {
            var result = _getPropertiesDictionary.GetOrCreateValue(ref _getPropertyLock, property, _getPropertyGetterDelegate);
            return result;
        }

        private static YieldReaderWriterLockSlim _getTypedPropertyLock;
        private static readonly Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate> _getTypedPropertiesDictionary = new Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate>();
        private static readonly Func<KeyValuePair<Type, PropertyInfo>, Delegate> _getTypedPropertyGetterDelegate = _GetPropertyGetterDelegate;
        /// <summary>
        /// Gets a delegate to read values from the given property in a very fast manner.
        /// The result will be already cast or will even avoid casts if the 
        /// generic types are correct.
        /// </summary>
        public static Func<TInstance, TOutput> GetPropertyGetterDelegate<TInstance, TOutput>(PropertyInfo property)
        {
            if (property == null)
                throw new ArgumentNullException("property");

            var pair = Pair.Create(typeof(Func<TInstance, TOutput>), property);
            var result = _getTypedPropertiesDictionary.GetOrCreateValue(ref _getTypedPropertyLock, pair, _getTypedPropertyGetterDelegate);
            return (Func<TInstance, TOutput>)result;
        }

        private static Delegate _GetPropertyGetterDelegate(KeyValuePair<Type, PropertyInfo> pair)
        {
            var funcType = pair.Key;
            var property = pair.Value;

            var funcArguments = funcType.GetGenericArguments();
            var instanceType = funcArguments[0];
            var resultType = funcArguments[1];

            var parameter = Expression.Parameter(instanceType, "instance");
            Expression resultExpression;

            var getMethod = property.GetGetMethod();
            if (getMethod == null)
                throw new ArgumentException("Property " + property.Name + " can't be read.", "read");

            if (getMethod.IsStatic)
                resultExpression = Expression.MakeMemberAccess(null, property);
            else
            {
                Expression readParameter = parameter;

                if (property.DeclaringType != instanceType)
                    readParameter = Expression.Convert(parameter, property.DeclaringType);

                resultExpression = Expression.MakeMemberAccess(readParameter, property);
            }

            if (property.PropertyType != resultType)
                resultExpression = Expression.Convert(resultExpression, resultType);

            var lambda = Expression.Lambda(funcType, resultExpression, parameter);

            var result = lambda.Compile();
            return result;
        }
        #endregion
        #region GetPropertySetterDelegate
        private static YieldReaderWriterLockSlim _setPropertyLock;
        private static readonly Dictionary<PropertyInfo, Action<object, object>> _setPropertiesDictionary = new Dictionary<PropertyInfo, Action<object, object>>();
        private static readonly Func<PropertyInfo, Action<object, object>> _getPropertySetterDelegate = GetPropertySetterDelegate<object, object>;
        /// <summary>
        /// Gets a delegate that can be used to do very fast sets on the given property.
        /// </summary>
        public static Action<object, object> GetPropertySetterDelegate(PropertyInfo property)
        {
            var result = _setPropertiesDictionary.GetOrCreateValue(ref _setPropertyLock, property, _getPropertySetterDelegate);
            return result;
        }

        private static YieldReaderWriterLockSlim _typedPropertySetterDelegatesLock;
        private static readonly Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate> _typedPropertySetterDelegatesDictionary = new Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate>();
        private static Func<KeyValuePair<Type, PropertyInfo>, Delegate> _typedGetPropertySetterDelegate = _TypedGetPropertySetterDelegate;
        /// <summary>
        /// Gets a delegate that can be used to do very fast sets on the given property.
        /// If the generic types are correct, casts can be avoided to improve performance
        /// even further.
        /// </summary>
        public static Action<TInstance, TValue> GetPropertySetterDelegate<TInstance, TValue>(PropertyInfo property)
        {
            if (property == null)
                throw new ArgumentNullException("property");

            var pair = Pair.Create(typeof(Action<TInstance, TValue>), property);
            var result = _typedPropertySetterDelegatesDictionary.GetOrCreateValue(ref _typedPropertySetterDelegatesLock, pair, _typedGetPropertySetterDelegate);
            return (Action<TInstance, TValue>)result;
        }

        private static Delegate _TypedGetPropertySetterDelegate(KeyValuePair<Type, PropertyInfo> pair)
        {
            var actionType = pair.Key;
            var property = pair.Value;
            var actionArguments = actionType.GetGenericArguments();
            var instanceType = actionArguments[0];
            var valueType = actionArguments[1];

            var instanceParameter = Expression.Parameter(instanceType, "instance");

            var valueParameter = Expression.Parameter(valueType, "value");
            Expression readValueParameter = valueParameter;
            if (property.PropertyType != valueType)
                readValueParameter = Expression.Convert(valueParameter, property.PropertyType);

            // .Net 3.5 does not have assign
            // but we can call the set method directly (and we need it to test for static).
            var setMethod = property.GetSetMethod(true);
            if (setMethod == null)
                throw new ArgumentException("Property " + property.Name + " is read-only.", "property");

            Expression setExpression;
            if (setMethod.IsStatic)
                setExpression = Expression.Call(setMethod, readValueParameter);
            else
            {
                Expression readInstanceParameter = instanceParameter;
                if (property.DeclaringType != instanceType)
                    readInstanceParameter = Expression.Convert(instanceParameter, property.DeclaringType);

                setExpression = Expression.Call(readInstanceParameter, setMethod, readValueParameter);
            }

            var lambda = Expression.Lambda(actionType, setExpression, instanceParameter, valueParameter);
            var result = lambda.Compile();
            return result;
        }
        #endregion
        #region GetEventAdder
        private static YieldReaderWriterLockSlim _addEventLock;
        private static readonly Dictionary<EventInfo, Action<object, Delegate>> _addEventDictionary = new Dictionary<EventInfo, Action<object, Delegate>>();
        private static readonly Func<EventInfo, Action<object, Delegate>> _getEventAdderDelegate = GetEventAdderDelegate<object, Delegate>;

        /// <summary>
        /// Gets a delegate to do fast "event add"
        /// </summary>
        public static Action<object, Delegate> GetEventAdderDelegate(EventInfo eventInfo)
        {
            var result = _addEventDictionary.GetOrCreateValue(ref _addEventLock, eventInfo, _getEventAdderDelegate);
            return result;
        }

        /// <summary>
        /// Gets a delegate to do fast "event add".
        /// Can avoid casts if the right generic types are given.
        /// </summary>
        public static Action<TInstance, TDelegate> GetEventAdderDelegate<TInstance, TDelegate>(EventInfo eventInfo)
        {
            return _GetEventDelegate<TInstance, TDelegate>(eventInfo.GetAddMethod(), eventInfo.EventHandlerType);
        }
        #endregion
        #region GetEventRemoverDelegate
        private static YieldReaderWriterLockSlim _removeEventLock;
        private static readonly Dictionary<EventInfo, Action<object, Delegate>> _removeEventDictionary = new Dictionary<EventInfo, Action<object, Delegate>>();
        private static readonly Func<EventInfo, Action<object, Delegate>> _getEventRemoverDelegate = GetEventRemoverDelegate<object, Delegate>;

        /// <summary>
        /// Gets a delegate to do fast "event remove" calls.
        /// </summary>
        public static Action<object, Delegate> GetEventRemoverDelegate(EventInfo eventInfo)
        {
            var result = _removeEventDictionary.GetOrCreateValue(ref _removeEventLock, eventInfo, _getEventRemoverDelegate);
            return result;
        }
        /// <summary>
        /// Gets a delegate to do fast "event remove" calls.
        /// Can avoid casts if the right generic types are given.
        /// </summary>
        public static Action<TInstance, TDelegate> GetEventRemoverDelegate<TInstance, TDelegate>(EventInfo eventInfo)
        {
            return _GetEventDelegate<TInstance, TDelegate>(eventInfo.GetRemoveMethod(), eventInfo.EventHandlerType);
        }
        #endregion
        #region _GetEventDelegate
        private static Action<TInstance, TDelegate> _GetEventDelegate<TInstance, TDelegate>(MethodInfo method, Type handlerType)
        {
            var instanceParameter = Expression.Parameter(typeof(TInstance), "instance");
            var handlerParameter = Expression.Parameter(typeof(TDelegate), "handler");
            Expression readHandlerParameter = handlerParameter;
            if (handlerType != typeof(TDelegate))
                readHandlerParameter = Expression.Convert(handlerParameter, handlerType);

            Expression callExpression;
            if (method.IsStatic)
                callExpression = Expression.Call(method, readHandlerParameter);
            else
            {
                Expression readInstanceParameter = instanceParameter;
                if (method.DeclaringType != typeof(TInstance))
                    readInstanceParameter = Expression.Convert(instanceParameter, method.DeclaringType);

                callExpression = Expression.Call(readInstanceParameter, method, readHandlerParameter);
            }

            var lambda = Expression.Lambda<Action<TInstance, TDelegate>>(callExpression, instanceParameter, handlerParameter);
            var result = lambda.Compile();
            return result;
        }
        #endregion

        #region GetFieldGetterDelegate
        private static YieldReaderWriterLockSlim _getFieldLock;
        private static readonly Dictionary<FieldInfo, Func<object, object>> _getFieldsDictionary = new Dictionary<FieldInfo, Func<object, object>>();
        private static readonly Func<FieldInfo, Func<object, object>> _getFieldGetterDelegate = GetFieldGetterDelegate<object, object>;
        /// <summary>
        /// Gets a delegate to read values from the given field in a very fast manner.
        /// </summary>
        public static Func<object, object> GetFieldGetterDelegate(FieldInfo field)
        {
            var result = _getFieldsDictionary.GetOrCreateValue(ref _getFieldLock, field, _getFieldGetterDelegate);
            return result;
        }

        private static YieldReaderWriterLockSlim _getTypedFieldLock;
        private static readonly Dictionary<KeyValuePair<Type, FieldInfo>, Delegate> _getTypedFieldsDictionary = new Dictionary<KeyValuePair<Type, FieldInfo>, Delegate>();
        private static readonly Func<KeyValuePair<Type, FieldInfo>, Delegate> _getTypedFieldGetterDelegate = _GetFieldGetterDelegate;
        /// <summary>
        /// Gets a delegate to read values from the given field in a very fast manner.
        /// The result will be already cast or will even avoid casts if the 
        /// generic types are correct.
        /// </summary>
        public static Func<TInstance, TOutput> GetFieldGetterDelegate<TInstance, TOutput>(FieldInfo field)
        {
            if (field == null)
                throw new ArgumentNullException("field");

            var pair = Pair.Create(typeof(Func<TInstance, TOutput>), field);
            var result = _getTypedFieldsDictionary.GetOrCreateValue(ref _getTypedFieldLock, pair, _getTypedFieldGetterDelegate);
            return (Func<TInstance, TOutput>)result;
        }
        private static Delegate _GetFieldGetterDelegate(KeyValuePair<Type, FieldInfo> pair)
        {
            var funcType = pair.Key;
            var field = pair.Value;

            var funcArguments = funcType.GetGenericArguments();
            var instanceType = funcArguments[0];
            var resultType = funcArguments[1];

            var parameter = Expression.Parameter(instanceType, "instance");
            Expression resultExpression;

            if (field.IsStatic)
                resultExpression = Expression.MakeMemberAccess(null, field);
            else
            {
                Expression readParameter = parameter;

                if (field.DeclaringType != instanceType)
                    readParameter = Expression.Convert(parameter, field.DeclaringType);

                resultExpression = Expression.MakeMemberAccess(readParameter, field);
            }

            if (field.FieldType != resultType)
                resultExpression = Expression.Convert(resultExpression, resultType);

            var lambda = Expression.Lambda(funcType, resultExpression, parameter);

            var result = lambda.Compile();
            return result;
        }
        #endregion

#if !WINDOWS_PHONE
        #region GetFastDynamicDelegate
        /// <summary>
        /// You have an untyped delegate? Then get another delegate to invoke it faster.
        /// </summary>
        public static FastDynamicDelegate GetFastDynamicDelegate(Delegate realDelegate)
        {
            var result = ReflectionHelper.GetMethodCallDelegate(realDelegate.Method);
            return (parameters) => result(realDelegate.Target, parameters);
        }
        #endregion
#endif
    }

    /// <summary>
    /// This is a typed version of reflection helper, so your expression already starts with a know
    /// object type (used when you don't have an already instantiated object).
    /// </summary>
    public static class ReflectionHelper<ForType>
    {
        #region GetMember
        /// <summary>
        /// Gets a member by it's expression usage.
        /// For example, GetMember((obj) => obj.GetType()) will return the
        /// GetType method.
        /// </summary>
        public static MemberInfo GetMember<T>(Expression<Func<ForType, T>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var body = expression.Body;

            switch (body.NodeType)
            {
                case ExpressionType.MemberAccess:
                    MemberExpression memberExpression = (MemberExpression)body;
                    return memberExpression.Member;

                case ExpressionType.Call:
                    MethodCallExpression callExpression = (MethodCallExpression)body;
                    return callExpression.Method;

                case ExpressionType.New:
                    NewExpression newExpression = (NewExpression)body;
                    return newExpression.Constructor;
            }

            throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
        }
        #endregion
        #region GetField
        /// <summary>
        /// Gets a field from a sample usage.
        /// Example: GetField((obj) => obj.SomeField) will return the FieldInfo of
        /// EmptyTypes.
        /// </summary>
        public static FieldInfo GetField<T>(Expression<Func<ForType, T>> expression)
        {
            return (FieldInfo)GetMember(expression);
        }
        #endregion
        #region GetProperty
        /// <summary>
        /// Gets a property from a sample usage.
        /// Example: GetProperty((str) => str.Length) will return the property info 
        /// of Length.
        /// </summary>
        public static PropertyInfo GetProperty<T>(Expression<Func<ForType, T>> expression)
        {
            return (PropertyInfo)GetMember(expression);
        }
        #endregion
        #region GetMethod
        /// <summary>
        /// Gets a method info of a void method.
        /// Example: GetMethod((obj) => obj.SomeCall("")); will return the
        /// MethodInfo of SomeCall that receives a single argument.
        /// </summary>
        public static MethodInfo GetMethod(Expression<Action<ForType>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            var body = expression.Body;
            if (body.NodeType != ExpressionType.Call)
                throw new ArgumentException("expression.Body must be a Call expression.", "expression");

            MethodCallExpression callExpression = (MethodCallExpression)body;
            return callExpression.Method;
        }

        /// <summary>
        /// Gets the MethodInfo of a method that returns a value.
        /// Example: GetMethod((obj) => obj.SomeCall()); will return the method info
        /// of SomeCall.
        /// </summary>
        public static MethodInfo GetMethod<T>(Expression<Func<ForType, T>> expression)
        {
            return (MethodInfo)GetMember(expression);
        }
        #endregion
    }
}
下载地址:






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值