ClayFactoryBehavior.cs 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using Microsoft.CSharp.RuntimeBinder;
  6. namespace ClaySharp.Behaviors {
  7. public class ClayFactoryBehavior : ClayBehavior {
  8. public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args) {
  9. dynamic shape = new Clay(
  10. new InterfaceProxyBehavior(),
  11. new PropBehavior(),
  12. new ArrayPropAssignmentBehavior(),
  13. new NilResultBehavior());
  14. shape.ShapeName = name;
  15. if (args.Positional.Count() == 1) {
  16. var options = args.Positional.Single();
  17. var assigner = GetAssigner(options.GetType());
  18. assigner.Invoke(shape, options);
  19. }
  20. foreach (var kv in args.Named) {
  21. shape[kv.Key] = kv.Value;
  22. }
  23. return shape;
  24. }
  25. private static Action<dynamic, object> GetAssigner(Type sourceType) {
  26. lock (_assignerCache) {
  27. Action<dynamic, object> assigner;
  28. if (_assignerCache.TryGetValue(sourceType, out assigner))
  29. return assigner;
  30. // given "sourceType T" with public properties, e.g. X and Y, generate the following lambda
  31. //
  32. // (dynamic target, object source) => {
  33. // target.X = ((T)source).X;
  34. // target.Y = ((T)source).Y;
  35. // }
  36. var targetParameter = Expression.Parameter(typeof(object), "target");
  37. var sourceParameter = Expression.Parameter(typeof(object), "source");
  38. // for each propertyInfo, e.g. X
  39. // produce dynamic call site, (target).X = ((T)source).X
  40. var assignments = sourceType.GetProperties().Select(
  41. property => Expression.Dynamic(
  42. Binder.SetMember(
  43. CSharpBinderFlags.None,
  44. property.Name,
  45. typeof(void),
  46. new[] {
  47. CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
  48. CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
  49. }),
  50. typeof(void),
  51. targetParameter,
  52. Expression.Property(
  53. Expression.Convert(sourceParameter, sourceType),
  54. property)));
  55. var lambda = Expression.Lambda<Action<dynamic, object>>(
  56. Expression.Block(assignments),
  57. targetParameter,
  58. sourceParameter);
  59. assigner = lambda.Compile();
  60. _assignerCache.Add(sourceType, assigner);
  61. return assigner;
  62. }
  63. }
  64. static readonly Dictionary<Type, Action<dynamic, object>> _assignerCache = new Dictionary<Type, Action<dynamic, object>>();
  65. }
  66. }