导航

10-001 Mvc.Common 之 PropertyHelper

Posted on 2015-04-09 14:39  DotNet1010  阅读(277)  评论(0)    收藏  举报

--

看一下方法:MakeFastPropertyGetter

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace Microsoft.AspNet.Mvc
{
    internal class PropertyHelper
    {
        // Delegate type for a by-ref property getter
        private delegate TValue ByRefFunc<TDeclaringType, TValue>(ref TDeclaringType arg);

        private static readonly MethodInfo CallPropertyGetterOpenGenericMethod =
            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod("CallPropertyGetter");

        private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod =
            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod("CallPropertyGetterByReference");

        private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
            typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod("CallPropertySetter");

        private static readonly ConcurrentDictionary<Type, PropertyHelper[]> ReflectionCache =
            new ConcurrentDictionary<Type, PropertyHelper[]>();

        private readonly Func<object, object> _valueGetter;

        /// <summary>
        /// Initializes a fast property helper.
        ///
        /// This constructor does not cache the helper. For caching, use GetProperties.
        /// </summary>
        public PropertyHelper([NotNull] PropertyInfo property)
        {
            Property = property;
            Name = property.Name;
            _valueGetter = MakeFastPropertyGetter(property);
        }

        public PropertyInfo Property { get; private set; }

        public virtual string Name { get; protected set; }

        public object GetValue(object instance)
        {
            return _valueGetter(instance);
        }

        /// <summary>
        /// Creates and caches fast property helpers that expose getters for every public get property on the
        /// underlying type.
        /// </summary>
        /// <param name="instance">the instance to extract property accessors for.</param>
        /// <returns>a cached array of all public property getters from the underlying type of target instance.
        /// </returns>
        public static PropertyHelper[] GetProperties(object instance)
        {
            return GetProperties(instance.GetType());
        }

        /// <summary>
        /// Creates and caches fast property helpers that expose getters for every public get property on the
        /// specified type.
        /// </summary>
        /// <param name="type">the type to extract property accessors for.</param>
        /// <returns>a cached array of all public property getters from the type of target instance.
        /// </returns>
        public static PropertyHelper[] GetProperties(Type type)
        {
            return GetProperties(type, CreateInstance, ReflectionCache);
        }

        /// <summary>
        /// Creates a single fast property getter. The result is not cached.
        /// </summary>
        /// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
        /// <returns>a fast getter.</returns>
        /// <remarks>
        /// This method is more memory efficient than a dynamically compiled lambda, and about the
        /// same speed.
        /// </remarks>
        public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
        {
            Debug.Assert(propertyInfo != null);

            var getMethod = propertyInfo.GetMethod;
            Debug.Assert(getMethod != null);
            Debug.Assert(!getMethod.IsStatic);
            Debug.Assert(getMethod.GetParameters().Length == 0);

			// Instance methods in the CLR can be turned into static methods where the first parameter
			// is open over "target". This parameter is always passed by reference, so we have a code
			// path for value types and a code path for reference types.

			// 要分成两类 值类型struct中的方法  引用类型Class中的方法


			/*
			   public class MyStruct()
				{
				    public string Name {get;set;}
			    }

			   typeInput=typeof(MyStruct);
			   typeOutput=typeof(string);

			    public class MyClass()
				{
				    public string Name {get;set;}
			    }

			   typeInput=typeof(MyClass);
			   typeOutput=typeof(string);
	        */

			var typeInput  = getMethod.DeclaringType;
            var typeOutput = getMethod.ReturnType;

            Delegate callPropertyGetterDelegate;
			if (typeInput.IsValueType()) // 是 struct 不是 class 
			{
				// Create a delegate (ref TDeclaringType) -> TValue
				var delegateType = typeof(ByRefFunc<,>).MakeGenericType(typeInput, typeOutput);
				// delegateType=typeof(ByRefFunc<MyStruct,string>);
				var propertyGetterAsFunc = getMethod.CreateDelegate(delegateType);
				//  delegate string propertyGetterAsFunc(ref MyStruct a)



				var callPropertyGetterClosedGenericMethod =
					CallPropertyGetterByReferenceOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput);
				/*
				---------open
					 private static object CallPropertyGetterByReference<TDeclaringType, TValue>(
               ByRefFunc<TDeclaringType, TValue> getter, object target)
           {
            var unboxed = (TDeclaringType)target;
            return getter(ref unboxed);
           }
		        callPropertyGetterClosedGenericMethod 下方
				private satic object CallPropertyGetterByReference(ByRefFunc<MyStruct,string> getter,object target)
				{
				     var unboxed = (MyStruct)target;
                     return getter(ref unboxed);
				}

 public class C
{
   private int id;
    public C(int id) { this.id = id; }

    public void M1(string s) 
    { 
        Console.WriteLine("Instance method M1 on C:  id = {0}, s = {1}",
            this.id, s);
    }

    public static void M2(string s)
    { 
        Console.WriteLine("Static method M2 on C:  s = {0}", s); 
    }
}

				   public delegate void D1(C c, string s);
                   public delegate void D2(string s);
                   public delegate void D3();

				   MethodInfo mi1 = typeof(C).GetMethod("M1", BindingFlags.Public | BindingFlags.Instance);
				   MethodInfo mi2 = typeof(C).GetMethod("M2", BindingFlags.Public | BindingFlags.Static);

				public static Delegate CreateDelegate (Type type,Object firstArgument,MethodInfo method,bool throwOnBindFailure)

				       Delegate test = Delegate.CreateDelegate(typeof(D2), c1, mi1, false);

		              d1 = (D1) Delegate.CreateDelegate(typeof(D1), null, mi1);

        // An instance of C must be passed in each time the 
        // delegate is invoked.
        //
        d1(c1, "Hello, World!");
        d1(new C(5280), "Hi, Mom!");

				   
		

	            */

				callPropertyGetterDelegate =
					callPropertyGetterClosedGenericMethod.CreateDelegate(
						typeof(Func<object, object>), propertyGetterAsFunc);
				/*
				  

			   private satic object CallPropertyGetterByReference(ByRefFunc<MyStruct,string> getter,object target)
			   要变成  string function (object) 

				         参数是  delegate string propertyGetterAsFunc(ref MyStruct a)
						        ByRefFunc<MyStruct,string> getter

			                     var unboxed = (ByRefFunc<MyStruct,string>)  delegate string propertyGetterAsFunc(ref MyStruct a);
								  return getter(ref unboxed);

			                      执行 delegate string propertyGetterAsFunc(ref MyStruct a);


			             

	            */



			}
			else  // 是class 
			{
				// Create a delegate TDeclaringType -> TValue
				var propertyGetterAsFunc =
					getMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(typeInput, typeOutput));

				var callPropertyGetterClosedGenericMethod =
					CallPropertyGetterOpenGenericMethod.MakeGenericMethod(typeInput, typeOutput);
				callPropertyGetterDelegate =
					callPropertyGetterClosedGenericMethod.CreateDelegate(
						typeof(Func<object, object>), propertyGetterAsFunc);
			}

            return (Func<object, object>)callPropertyGetterDelegate;
        }

        /// <summary>
        /// Creates a single fast property setter for reference types. The result is not cached.
        /// </summary>
        /// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
        /// <returns>a fast getter.</returns>
        /// <remarks>
        /// This method is more memory efficient than a dynamically compiled lambda, and about the
        /// same speed. This only works for reference types.
        /// </remarks>
        public static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
        {
            Debug.Assert(propertyInfo != null);
            Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType);

            var setMethod = propertyInfo.SetMethod;
            Debug.Assert(setMethod != null);
            Debug.Assert(!setMethod.IsStatic);
            Debug.Assert(setMethod.ReturnType == typeof(void));
            var parameters = setMethod.GetParameters();
            Debug.Assert(parameters.Length == 1);

            // Instance methods in the CLR can be turned into static methods where the first parameter
            // is open over "target". This parameter is always passed by reference, so we have a code
            // path for value types and a code path for reference types.
            var typeInput = setMethod.DeclaringType;
            var parameterType = parameters[0].ParameterType;

            // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
            var propertySetterAsAction =
                setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
            var callPropertySetterClosedGenericMethod =
                CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
            var callPropertySetterDelegate =
                callPropertySetterClosedGenericMethod.CreateDelegate(
                    typeof(Action<object, object>), propertySetterAsAction);

            return (Action<object, object>)callPropertySetterDelegate;
        }

        private static PropertyHelper CreateInstance(PropertyInfo property)
        {
            return new PropertyHelper(property);
        }

        // Called via reflection
        private static object CallPropertyGetter<TDeclaringType, TValue>(
            Func<TDeclaringType, TValue> getter,
            object target)
        {
            return getter((TDeclaringType)target);
        }

        // Called via reflection
        private static object CallPropertyGetterByReference<TDeclaringType, TValue>(
            ByRefFunc<TDeclaringType, TValue> getter,
            object target)
        {
            var unboxed = (TDeclaringType)target;
            return getter(ref unboxed);
        }

        private static void CallPropertySetter<TDeclaringType, TValue>(
            Action<TDeclaringType, TValue> setter,
            object target,
            object value)
        {
            setter((TDeclaringType)target, (TValue)value);
        }

        protected static PropertyHelper[] GetProperties(
            Type type,
            Func<PropertyInfo, PropertyHelper> createPropertyHelper,
            ConcurrentDictionary<Type, PropertyHelper[]> cache)
        {
            // Unwrap nullable types. This means Nullable<T>.Value and Nullable<T>.HasValue will not be
            // part of the sequence of properties returned by this method.
            type = Nullable.GetUnderlyingType(type) ?? type;

            // Using an array rather than IEnumerable, as target will be called on the hot path numerous times.
            PropertyHelper[] helpers;

            if (!cache.TryGetValue(type, out helpers))
            {
                // We avoid loading indexed properties using the where statement.
                // Indexed properties are not useful (or valid) for grabbing properties off an object.
                var properties = type.GetRuntimeProperties().Where(
                    prop => prop.GetIndexParameters().Length == 0 &&
                    prop.GetMethod != null &&
                    prop.GetMethod.IsPublic &&
                    !prop.GetMethod.IsStatic);

                helpers = properties.Select(p => createPropertyHelper(p)).ToArray();
                cache.TryAdd(type, helpers);
            }

            return helpers;
        }
    }
}