/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

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