Пример #1
0
        private static void HandleFieldConstant(Set <UsedPropertyChain> container,
                                                Func <object> constantObjectRetriever,
                                                ChildInformation child,
                                                TraversalOptions options)
        {
            INotifyPropertyChanged notifiable;
            Func <object>          targetRetriever;

            if (child.Property != null)
            {
                //This occurs in case of expression like:
                //React.To(() => recursive.PropertyA).Set(...);
                //or with marker methods:
                //React.To(() => recursive.TrackFields().PropertyA).Set(...);
                notifiable      = child.Property.GetCurrentParent();
                targetRetriever = constantObjectRetriever;
            }
            else
            {
                //This case occurs, when a method is called on a tracked field, e.g.:
                //React.To(() => recursive.TrackFields().GetInner().PropertyA).Set(...);
                //The call to GetInner() breaks the chain and there is no tracked child
                //for the 'recursive' object. However, this is still a viable case;
                //if recursive is INPC, track all properties on it, otherwise track nothing.
                Func <object> parentRetriever = constantObjectRetriever;
                notifiable      = parentRetriever() as INotifyPropertyChanged;
                targetRetriever = parentRetriever;
            }

            if (notifiable != null && child.IsTrackable) //property and method children are trackable
            {
                //Do not allow fields here, because they cannot be tracked anyhow.

                //string.Empty here as the property name does not mean all properties
                //of the associated object will be tracked. It only means that associated
                //object is stored in a field and changes to that field cannot be tracked,
                //just because it's not a property. Thus, string.Empty indicates lack
                //of any property name.
                var chain = new UsedPropertyChain(notifiable, string.Empty, child.Property, options);
                chain.TargetRetriever = targetRetriever;
                container.Add(chain);
            }

            //Otherwise, track nothing
        }
Пример #2
0
        private static void GetUsedProperties(Set <UsedPropertyChain> container,
                                              MemberExpression memberExpression,
                                              ChildInformation child,
                                              TraversalOptions options)
        {
            if (memberExpression == null)
            {
                return;
            }
            MemberTypes memberType = memberExpression.Member.MemberType;

            if (memberType != MemberTypes.Property)
            {
                if (memberType == MemberTypes.Field)
                {
                    if (options.TrackFields)
                    {
                        var constant = memberExpression.Expression as ConstantExpression;
                        if (constant != null)
                        {
                            HandleFieldConstant(container, memberExpression, child, options);
                            return;
                        }

                        if (IsFieldChain(memberExpression) && child.IsTrackable)
                        {
                            //Occurs with a.b.c.A kind of expression, where
                            //the field chain is the leading part of the
                            //expression and the constant can be extracted from
                            //it. The constant is taken from evaluation of a.b.c

                            HandleFieldConstant(container, memberExpression, child, options);
                            return;
                        }
                        else
                        {
                            //Dead end. It happens, when a field occurs
                            //after a non-constant element, e.g. after
                            //a property reference as in 'A.B.field.C'
                            //'A.B' is not ConstantExpression and therefore,
                            //even with TrackFields on, the field cannot be
                            //tracked any further. However, the preceding
                            //part of the expression can and should be
                            //tracked.

                            //Break the chain (without continuation)
                            GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options);
                            return;
                        }
                    }
                    else
                    {
                        //Break the chain (without continuation)
                        GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options);
                        return;
                    }
                }

                throw new InvalidOperationException("Unhandled case. Revise the code");
            }

            var parentMember = memberExpression.Expression as MemberExpression;

            if (parentMember != null)
            {
                UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options);
                var             usedChild       = new ChildInformation(usedSubProperty);
                //Continue the chain
                GetUsedProperties(container, parentMember, usedChild, options);
            }
            else
            {
                if (memberExpression.Expression is ParameterExpression)
                {
                    //Presumably, we are within a nested lambda parameter block.
                    //Skip, just for now. If there's a nested lambda like "c=>c.property" it
                    //is pretty complicated to know when to update.
                    //TODO:
                    return;
                }

                var constant = memberExpression.Expression as ConstantExpression;
                if (constant != null)
                {
                    var notifiable = constant.Value as INotifyPropertyChanged;
                    if (notifiable != null)
                    {
                        string propertyName = memberExpression.Member.Name;
                        var    chain        = new UsedPropertyChain(notifiable, propertyName, child.Property, options);
                        chain.TargetRetriever = CompileMemberExpression(memberExpression);
                        container.Add(chain);
                    }
                    else
                    {
                        //Even now add a chain to the container. Fields are not traced, but their
                        //properties can be. This applies only to cases where a the left-hand part
                        //of an expression is a field or a field/constant chain.
                        HandleFieldConstant(container, memberExpression, child, options);
                    }
                    return;
                }
                else
                {
                    UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options);
                    var             usedChild       = new ChildInformation(usedSubProperty);

                    //This should break the notification chain or continue, if it's marker method.
                    GetUsedProperties(container, memberExpression.Expression, usedChild, options);
                }
            }
        }