PageRenderTime 101ms CodeModel.GetById 93ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

/src/LinFu.IoC/Configuration/PropertySetter.cs

http://github.com/philiplaureano/LinFu
C# | 104 lines | 59 code | 20 blank | 25 comment | 9 complexity | 8b1e1587860f6d5a4b0ce53d544a363d MD5 | raw file
  1using System;
  2using System.Collections.Generic;
  3using System.Reflection;
  4using System.Reflection.Emit;
  5using LinFu.IoC.Configuration.Interfaces;
  6using LinFu.Reflection;
  7
  8namespace LinFu.IoC.Configuration
  9{
 10    /// <summary>
 11    ///     A class responsible for setting property values.
 12    /// </summary>
 13    [Implements(typeof(IPropertySetter), LifecycleType.OncePerRequest)]
 14    public class PropertySetter : IPropertySetter
 15    {
 16        private static readonly Dictionary<PropertyInfo, Action<object, object>> _setters =
 17            new Dictionary<PropertyInfo, Action<object, object>>();
 18
 19        private static readonly Type[] _parameterTypes = {typeof(object), typeof(object)};
 20
 21
 22        /// <summary>
 23        ///     Sets the value of the <paramref name="targetProperty" />.
 24        /// </summary>
 25        /// <param name="target">The target instance that contains the property to be modified.</param>
 26        /// <param name="targetProperty">The property that will store the given value.</param>
 27        /// <param name="value">The value that will be assigned to the property.</param>
 28        public void Set(object target, PropertyInfo targetProperty, object value)
 29        {
 30            if (target == null)
 31                throw new ArgumentNullException("target");
 32
 33            // Reuse the cached results, if possible
 34            Action<object, object> setter = null;
 35            if (_setters.ContainsKey(targetProperty))
 36            {
 37                setter = _setters[targetProperty];
 38                setter(target, value);
 39                return;
 40            }
 41
 42            setter = GenerateSetter(targetProperty);
 43
 44            lock (_setters)
 45            {
 46                _setters[targetProperty] = setter;
 47            }
 48
 49            if (setter != null)
 50                setter(target, value);
 51        }
 52
 53
 54        /// <summary>
 55        ///     Generates an <see cref="Action{T1, T2}" /> delegate that will be used
 56        ///     as the property setter for a particular type.
 57        /// </summary>
 58        /// <param name="targetProperty">The property that will be modified.</param>
 59        /// <returns>A property setter.</returns>
 60        private static Action<object, object> GenerateSetter(PropertyInfo targetProperty)
 61        {
 62            var setterMethod = targetProperty.GetSetMethod();
 63
 64            if (setterMethod == null)
 65                throw new ArgumentException(string.Format("The property '{0}' is missing a setter method!",
 66                    targetProperty));
 67
 68            // Validate the setter method
 69            if (!setterMethod.IsPublic)
 70                throw new ArgumentException(
 71                    string.Format("The property '{0}' must have a publicly visible setter in order to be modified",
 72                        targetProperty));
 73
 74            // HACK: Manually invoke the setter since the Mono runtime currently 
 75            // does not support the DynamicMethod class
 76            if (Runtime.IsRunningOnMono)
 77                return (target, value) => setterMethod.Invoke(target, new[] {value});
 78
 79            var dynamicMethod = new DynamicMethod(string.Empty, typeof(void), _parameterTypes);
 80            var IL = dynamicMethod.GetILGenerator();
 81
 82            // Push the target instance onto the stack
 83            IL.Emit(OpCodes.Ldarg_0);
 84
 85            // Cast it to the appropriate type
 86            IL.Emit(OpCodes.Isinst, targetProperty.DeclaringType);
 87
 88            // NOTE: A null reference check was intentionally omitted to make sure that the program
 89            // crashes if the instance type is of the wrong type
 90
 91            // Push the setter value
 92            IL.Emit(OpCodes.Ldarg_1);
 93            IL.Emit(OpCodes.Isinst, targetProperty.PropertyType);
 94
 95            // Call the setter
 96            var callInstruction = setterMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call;
 97            IL.Emit(callInstruction, setterMethod);
 98            IL.Emit(OpCodes.Ret);
 99
100            var setter = (Action<object, object>) dynamicMethod.CreateDelegate(typeof(Action<object, object>));
101            return setter;
102        }
103    }
104}