Beispiel #1
0
        public Expression Build(Expression element, MethodInfo method)
        {
            var type = method.DeclaringType;

            if (method.ReturnType != typeof(void) && method.ReturnType != typeof(EffectCleanupDelegate))
            {
                throw new ArgumentException($"Effect method {method} in {type} returns unsupported type {method.ReturnType}. It must return either void or {nameof(EffectCleanupDelegate)}.");
            }

            // find dependency fields
            var fields = new FieldInfo[_depFields.Length];

            for (var i = 0; i < _depFields.Length; i++)
            {
                fields[i] = type?.GetAllField(_depFields[i]) ?? throw new ArgumentException($"Cannot find field '{_depFields[i]}' in {type}.");
            }

            // find parameter providers
            var parameters = method.GetParameters();
            var arguments  = new Expression[parameters.Length];

            for (var i = 0; i < parameters.Length; i++)
            {
                arguments[i] = parameters[i].GetCustomAttributes().OfType <IBinderMethodParameterProvider>().FirstOrDefault()?.GetValue(element, parameters[i]) ?? throw new ArgumentException($"No provider configured for parameter {parameters} of method {method} in {type}.");
            }

            var body = new List <Expression>();

            // build dependency array
            var deps = Expression.Variable(typeof(object[]), "deps");

            if (Once)
            {
                body.Add(Expression.Assign(deps, Expression.Constant(null, typeof(object[]))));
            }
            else
            {
                var items = new Expression[fields.Length + arguments.Length];

                for (var i = 0; i < fields.Length; i++)
                {
                    items[i] = Expression.Field(element, fields[i]);
                }

                for (var i = 0; i < arguments.Length; i++)
                {
                    items[fields.Length + i] = arguments[i];
                }

                body.Add(Expression.Assign(deps, Expression.NewArrayInit(typeof(object), items.Select(x => Expression.Convert(x, typeof(object))))));
            }

            // create effect info
            var effect = Expression.Variable(typeof(RefObject <EffectInfo>), "effectObj");

            body.Add(Expression.Assign(effect, ElementBinder.BuildContainerInstantiation(typeof(RefObject <EffectInfo>), element, Expression.Constant($"effect:{method.Name}"), null)));

            // call effect set method
            body.Add(Expression.Call(
                         Expression.Coalesce(
                             ElementBinder.BuildContainerValueAccess(effect),
                             Expression.Assign(ElementBinder.BuildContainerValueAccess(effect), Expression.New(typeof(EffectInfo)))),
                         _effectInfoSetMethod,
                         ElementBinder.BuildElementNodeAccess(element),
                         Expression.Lambda <EffectDelegate>(buildEffect()),
                         deps));

            Expression buildEffect()
            {
                var result = Expression.Call(element, method, arguments);

                if (method.ReturnType == typeof(EffectCleanupDelegate))
                {
                    return(result);
                }

                return(Expression.Block(result, Expression.Constant(null, typeof(EffectCleanupDelegate))));
            }

            return(Expression.Block(new[] { deps, effect }, body));
        }