コード例 #1
0
        /// <summary>
        /// Builds an expression for the specified <see cref="ConstructorBinding" />.
        /// Called by <see cref="Build(ConstructorTarget, IExpressionCompileContext, IExpressionCompiler)" />
        /// </summary>
        /// <param name="binding">The binding.</param>
        /// <param name="context">The context.</param>
        /// <param name="compiler">The compiler to be used to build the target.</param>
        /// <remarks>The returned expression will either be a NewExpression or a MemberInitExpression</remarks>
        protected virtual Expression Build(ConstructorBinding binding, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            var newExpr = Expression.New(binding.Constructor,
                                         binding.BoundArguments.Select(
                                             a => compiler.Build(a.Target, context.NewContext(a.Parameter.ParameterType))));

            if (binding.MemberBindings.Length == 0)
            {
                return(newExpr);
            }
            else
            {
                ParameterExpression     localVar       = null;
                List <MemberAssignment> memberBindings = new List <MemberAssignment>();
                List <Expression>       adHocBindings  = new List <Expression>();

                foreach (var mb in binding.MemberBindings)
                {
                    // as soon as we have one list binding (which we can actually implement
                    // using the list binding expression) we need to capture the locally newed
                    // object into a local variable and pass it to the function below
                    if (mb is ListMemberBinding listBinding)
                    {
                        if (localVar == null)
                        {
                            localVar = Expression.Parameter(newExpr.Type, "toReturn");
                        }

                        adHocBindings.Add(GenerateListBindingExpression(localVar, listBinding, context, compiler));
                    }
                    else
                    {
                        memberBindings.Add(Expression.Bind(mb.Member, compiler.Build(mb.Target, context.NewContext(mb.MemberType))));
                    }
                }

                Expression toReturn = newExpr;

                if (memberBindings.Count != 0)
                {
                    toReturn = Expression.MemberInit(newExpr, memberBindings);
                }

                if (adHocBindings.Count != 0)
                {
                    List <Expression> blockCode = new List <Expression>
                    {
                        Expression.Assign(localVar, toReturn)
                    };
                    blockCode.AddRange(adHocBindings);
                    blockCode.Add(localVar);
                    toReturn = Expression.Block(new[] { localVar }, blockCode);
                }

                return(toReturn);
            }
        }
コード例 #2
0
        /// <summary>
        /// Builds an expression for the given <paramref name="target"/>.
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
        /// parameter is optional, this will always be provided</param>
        /// <exception cref="System.InvalidOperationException"></exception>
        protected override Expression Build(ResolvedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            var staticTarget = target.Bind(context);

            Expression staticExpr;

            if (staticTarget != null)
            {
                staticExpr = compiler.Build(staticTarget, context.NewContext(target.DeclaredType));

                if (staticExpr == null)
                {
                    throw new InvalidOperationException(string.Format(ExceptionResources.TargetReturnedNullExpressionFormat, staticTarget.GetType(), context.TargetType));
                }

                if (staticExpr.Type != target.DeclaredType)
                {
                    staticExpr = Expression.Convert(staticExpr, target.DeclaredType);
                }
            }
            else
            {
                // this should generate a missing dependency exception if executed
                // or, might actually yield a result if registrations have been added
                // after the expression is compiled.
                staticExpr = Methods.CallResolveContext_Resolve_Strong_Method(
                    context.ResolveContextParameterExpression,
                    target.DeclaredType);
            }

            return(staticExpr);
        }
コード例 #3
0
        /// <summary>
        /// Builds an expression which represents an instance of <see cref="IEnumerable{T}"/> whose elements are created by the
        /// <see cref="EnumerableTarget.Targets"/> of the passed <paramref name="target"/>.
        /// </summary>
        /// <param name="target">The target for which an expression is to be built.</param>
        /// <param name="context">The current compilation context.</param>
        /// <param name="compiler">The compiler to use when building expressions for child targets.</param>
        /// <returns>An expression which can be compiled into a delegate that, when executed, will create an instance of the enumerable
        /// represented by <paramref name="target"/>
        /// </returns>
        /// <remarks>
        /// The compiler is capable of producing both lazy-loaded and eager-loaded enumerables, which can be controlled via
        /// target container options.
        ///
        /// ## Lazy vs Eager loading
        ///
        /// The option <see cref="Options.LazyEnumerables"/> is read from the <paramref name="context"/> for the
        /// <see cref="EnumerableTarget.ElementType"/> of the <paramref name="target"/>.  If it is equivalent to <c>true</c>
        /// (the <see cref="Options.LazyEnumerables.Default"/>), then a lazily-loaded enumerable is constructed which will
        /// create new instances of each object in the enumerable each time it is enumerated.
        ///
        /// If the option is instead equivalent to <c>false</c>, then all instances will be created in advance, and an already-materialised
        /// enumerable is constructed.</remarks>
        protected override Expression Build(EnumerableTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            if (context.GetOption(target.ElementType, Options.LazyEnumerables.Default))
            {
                var funcs =
                    target.Targets.Select(t => compiler.BuildResolveLambdaStrong(t, context.NewContext(target.ElementType)).Compile())
                    .ToArray();

                var lazyType = typeof(LazyEnumerable <>).MakeGenericType(target.ElementType);

                var ctor = lazyType.GetConstructor(new[] { typeof(Delegate[]) });

                var lazy = ctor.Invoke(new object[] { funcs });

                return(Expression.Call(
                           Expression.Constant(lazy),
                           "GetInstances",
                           null,
                           context.ResolveContextParameterExpression));
            }
            else
            {
                List <Expression> all = new List <Expression>();

                for (var f = 0; f < target.Targets.Length; f++)
                {
                    all.Add(compiler.Build(target.Targets[f], context.NewContext(target.ElementType)));
                }

                return(Expression.New(
                           typeof(EagerEnumerable <>).MakeGenericType(target.ElementType).GetConstructors()[0],
                           Expression.NewArrayInit(target.ElementType, all)));
            }
        }
コード例 #4
0
        /// <summary>
        /// Builds the conversion expression represented by the <paramref name="target"/>
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.</param>
        protected override Expression Build(VariantMatchTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            // Here, the target type is *key*, especially for singletons


            return(Expression.Convert(compiler.Build(target.Target,
                                                     context.NewContext(target.RegisteredType, scopeBehaviourOverride: context.ScopeBehaviourOverride)), target.RequestedType));
        }
コード例 #5
0
 /// <summary>
 /// Builds the conversion expression represented by the <paramref name="target"/>
 /// </summary>
 /// <param name="target">The target whose expression is to be built.</param>
 /// <param name="context">The compilation context.</param>
 /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
 /// which might be required by the <paramref name="target" />.</param>
 protected override Expression Build(ChangeTypeTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
 {
     // build the inner target's expression; and wrap it in a conversion expression for the
     // target type of the ChangeTypeTarget.
     // note that if the compilation context was overriding the scoping behaviour before - then we pass that through,
     // because this target defaults to 'None'
     return(Expression.Convert(compiler.Build(target.InnerTarget,
                                              context.NewContext(target.InnerTarget.DeclaredType, scopeBehaviourOverride: context.ScopeBehaviourOverride)), target.DeclaredType));
 }
コード例 #6
0
        /// <summary>
        /// Builds an expression from the specified target for the given <see cref="IExpressionCompileContext" />
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
        /// parameter is optional, this will always be provided</param>
        /// <exception cref="NotImplementedException"></exception>
        protected override Expression Build(DelegateTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            var bindings = ParameterBinding.BindWithRezolvedArguments(target.Factory.GetMethodInfo());

            return(Expression.Invoke(Expression.Constant(target.Factory),
                                     bindings.Select(b => b.Parameter.ParameterType == typeof(ResolveContext) ?
                                                     context.ResolveContextParameterExpression
                    : compiler.Build(b.Target, context.NewContext(b.Parameter.ParameterType)))));
        }
コード例 #7
0
        /// <summary>
        /// Builds an expression for the given <paramref name="target"/>.
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
        /// parameter is optional, this will always be provided</param>
        protected override Expression Build(ScopedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            // all we need to do is force the inner target's scope behaviour to None - and this builder's
            // base code will ensure that the whole resulting expression is converted into an explicitly scoped one

            // note that this scope deactivation is only in place for this one target - if it has any child targets then
            // scoping behaviour for those returns to normal if compiled with a new context (which they always should be)

            return(compiler.Build(target.InnerTarget, context.NewContext(scopeBehaviourOverride: ScopeBehaviour.None)));
        }
コード例 #8
0
        /// <summary>
        /// Builds an expression for the passed <paramref name="target"/>
        /// </summary>
        /// <param name="target"></param>
        /// <param name="context"></param>
        /// <param name="compiler"></param>
        /// <returns></returns>
        protected override Expression Build(ProjectionTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            // functionally the same as the DecoratorTargetBuilder
            var newContext = context.NewContext(target.ImplementationType);

            newContext.Register(target.InputTarget, target.InputType ?? target.InputTarget.DeclaredType);

            // projection target acts as an anchor for the target it wraps - this allows a single registered
            // target which is either a singleton or scoped to be reused for multiple input targets.
            newContext.SetOption(new TargetIdentityOverride(target.Id), target.DeclaredType);
            return(compiler.Build(target.OutputTarget, newContext));
        }
コード例 #9
0
        /// <summary>
        /// Similar to <see cref="BuildResolveLambda(IExpressionCompiler, ITarget, IExpressionCompileContext)"/>, except this builds a
        /// lambda whose type is a strongly-typed delegate instead of object - i.e. Func{ResolveContext, T}
        /// </summary>
        /// <param name="compiler"></param>
        /// <param name="target"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public static LambdaExpression BuildResolveLambdaStrong(this IExpressionCompiler compiler, ITarget target, IExpressionCompileContext context)
        {
            if (compiler == null)
            {
                throw new ArgumentNullException(nameof(compiler));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            var expression = compiler.Build(target, context);

            return(compiler.BuildStrongFactoryLambda(expression, context));
        }
コード例 #10
0
        /// <summary>
        /// This method is a shortcut for building a lambda expression directly from an <see cref="ITarget" />.
        /// It calls <see cref="IExpressionCompiler.Build(ITarget, IExpressionCompileContext)" /> and passes the result to
        /// <see cref="IExpressionCompiler.BuildObjectFactoryLambda(Expression, IExpressionCompileContext)" />, which should yield an
        /// optimised lambda expression for the expression produced from the target which can then be
        /// compiled and used as the factory for that target.
        /// </summary>
        /// <param name="compiler">The compiler.</param>
        /// <param name="target">The target.</param>
        /// <param name="context">The current compilation context.</param>
        public static Expression <Func <ResolveContext, object> > BuildResolveLambda(this IExpressionCompiler compiler, ITarget target, IExpressionCompileContext context)
        {
            if (compiler == null)
            {
                throw new ArgumentNullException(nameof(compiler));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            var expression = compiler.Build(target, context);

            return(compiler.BuildObjectFactoryLambda(expression, context));
        }
コード例 #11
0
        /// <summary>
        /// Creates a new compilation context, registers the target's <see cref="DecoratorTarget.DecoratedTarget"/>
        /// into it as the correct target for the <see cref="DecoratorTarget.DecoratedType"/>, and then builds the
        /// expression for the <see cref="DecoratorTarget.InnerTarget"/> (which is typically a constructor target).
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
        /// parameter is optional, this will always be provided</param>
        protected override Expression Build(DecoratorTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            // need a new context for this, into which we can override the registration of the decorated type
            // to be the decorated target so that the decorator target will resolve that.
            // there's a potential hole here in that if another container resolves this same decorator after it
            // has been compiled, it might end up decorating itself - might need a test scenario for that.

            var newContext = context.NewContext();

            // add the decorated target into the compile context under the type which the enclosing decorator
            // was registered against.  If the inner target is bound to a type which correctly implements the decorator
            // pattern over the common decorated type, then the decorated instance should be resolved when constructor
            // arguments are resolved.
            newContext.Register(target.DecoratedTarget, target.DecoratedType);
            // TODO: Do the same target anchoring that SingletonTargetBuilder is doing.
            return(compiler.Build(target.InnerTarget, newContext));
        }
コード例 #12
0
        /// <summary>
        /// Builds an expression which either represents creating an array or a list of objects using an
        /// enumerable of targets from the <paramref name="target"/>'s <see cref="ListTarget.Items"/>.
        ///
        /// The target's <see cref="ListTarget.AsArray"/> flag is used to determine which expression to build.
        /// </summary>
        /// <param name="target">The target whose expression is to be built.</param>
        /// <param name="context">The compilation context.</param>
        /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
        /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
        /// parameter is optional, this will always be provided</param>
        protected override Expression Build(ListTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            var items = new List <Expression>();

            foreach (var itemTarget in target.Items)
            {
                items.Add(compiler.Build(itemTarget, context.NewContext(target.ElementType)));
            }

            var arrayExpr = Expression.NewArrayInit(target.ElementType, items);

            if (target.AsArray)
            {
                return(arrayExpr);
            }
            else
            {
                return(Expression.New(target.ListConstructor, arrayExpr));
            }
        }
コード例 #13
0
        private Expression GenerateListBindingExpression(Expression targetObj, ListMemberBinding listBinding, IExpressionCompileContext context, IExpressionCompiler compiler)
        {
            var method           = AddToCollection_Method.MakeGenericMethod(listBinding.ElementType);
            var enumType         = typeof(IEnumerable <>).MakeGenericType(listBinding.ElementType);
            var enumerable       = compiler.Build(listBinding.Target, context.NewContext(enumType));
            var enumLocal        = Expression.Parameter(enumType, "enumerable");
            var enumAssign       = Expression.Assign(enumLocal, enumerable);
            var addDelegateParam = Expression.Parameter(listBinding.ElementType, "item");

            var callAddToCollection = Expression.Call(null,
                                                      method,
                                                      Expression.Lambda(
                                                          Expression.Call(
                                                              listBinding.Member is PropertyInfo prop ? Expression.Property(targetObj, prop) : Expression.Field(targetObj, (FieldInfo)listBinding.Member),
                                                              listBinding.AddMethod,
                                                              addDelegateParam),
                                                          addDelegateParam),
                                                      enumLocal);

            return(Expression.Block(new[] { enumLocal, addDelegateParam },
                                    enumAssign,
                                    callAddToCollection));
        }
コード例 #14
0
 /// <summary>
 /// Obtains the bound target for the <paramref name="target"/> passed (by calling
 /// <see cref="GenericConstructorTarget.Bind(ICompileContext)"/>, and passes it to the
 /// <paramref name="compiler"/> to have an expression built for it.
 /// </summary>
 /// <param name="target">The target whose expression is to be built.</param>
 /// <param name="context">The compilation context.</param>
 /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
 /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
 /// parameter is optional, this will always be provided</param>
 protected override Expression Build(GenericConstructorTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
 {
     // simply bind the generic target to the context, obtain the target that is produced
     // and then build it.
     return(compiler.Build(target.Bind(context), context));
 }
コード例 #15
0
 /// <summary>
 /// Builds an expression for the given <paramref name="target"/>.
 /// </summary>
 /// <param name="target">The target whose expression is to be built.</param>
 /// <param name="context">The compilation context.</param>
 /// <param name="compiler">The expression compiler to be used to build any other expressions for targets
 /// which might be required by the <paramref name="target" />.  Note that unlike on the interface, where this
 /// parameter is optional, this will always be provided</param>
 protected override Expression Build(UnscopedTarget target, IExpressionCompileContext context, IExpressionCompiler compiler)
 {
     // force scoping off for the inner target
     return(compiler.Build(target.Inner, context.NewContext(scopeBehaviourOverride: ScopeBehaviour.None)));
 }