using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Microsoft.CSharp.RuntimeBinder; namespace ClaySharp.Behaviors { public class ClayFactoryBehavior : ClayBehavior { public override object InvokeMember(Func proceed, object self, string name, INamedEnumerable args) { dynamic shape = new Clay( new InterfaceProxyBehavior(), new PropBehavior(), new ArrayPropAssignmentBehavior(), new NilResultBehavior()); shape.ShapeName = name; if (args.Positional.Count() == 1) { var options = args.Positional.Single(); var assigner = GetAssigner(options.GetType()); assigner.Invoke(shape, options); } foreach (var kv in args.Named) { shape[kv.Key] = kv.Value; } return shape; } private static Action GetAssigner(Type sourceType) { lock (_assignerCache) { Action assigner; if (_assignerCache.TryGetValue(sourceType, out assigner)) return assigner; // given "sourceType T" with public properties, e.g. X and Y, generate the following lambda // // (dynamic target, object source) => { // target.X = ((T)source).X; // target.Y = ((T)source).Y; // } var targetParameter = Expression.Parameter(typeof(object), "target"); var sourceParameter = Expression.Parameter(typeof(object), "source"); // for each propertyInfo, e.g. X // produce dynamic call site, (target).X = ((T)source).X var assignments = sourceType.GetProperties().Select( property => Expression.Dynamic( Binder.SetMember( CSharpBinderFlags.None, property.Name, typeof(void), new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }), typeof(void), targetParameter, Expression.Property( Expression.Convert(sourceParameter, sourceType), property))); var lambda = Expression.Lambda>( Expression.Block(assignments), targetParameter, sourceParameter); assigner = lambda.Compile(); _assignerCache.Add(sourceType, assigner); return assigner; } } static readonly Dictionary> _assignerCache = new Dictionary>(); } }