/// <summary>
        /// Gets diff of two property containers.
        /// </summary>
        public static ObjectDiff GetDiff(
            IPropertyContainer container1,
            IPropertyContainer container2,
            Func <IPropertyValue, string>?renderValue = null,
            bool compareByRenderedValues = false,
            SearchOptions?searchOptions  = null)
        {
            renderValue ??= FormatForDiff;
            var search = searchOptions ?? Search.ExistingOnlyWithParent.WithPropertyComparer(PropertyComparer.ByTypeAndNameComparer);

            IEnumerable <(IPropertyValue?PropertyValue1, IPropertyValue PropertyValue2)> propertyPairs =
                container2
                .Properties
                .Select(propertyValue2 => (container1.GetPropertyValueUntyped(propertyValue2.PropertyUntyped, search), propertyValue2));

            IEnumerable <(IPropertyValue PropertyValue1, IPropertyValue?PropertyValue2)> removedPropertyPairs =
                container1
                .Properties
                .Select(propertyValue1 => (propertyValue1, container2.GetPropertyValueUntyped(propertyValue1.PropertyUntyped, search)))
                .Where(tuple => tuple.Item2 == null);

            var propertyPairsAll = propertyPairs.Concat(removedPropertyPairs);

            PropertyDiff[]? diffPairs;

            if (compareByRenderedValues)
            {
                diffPairs = propertyPairsAll
                            .Select(propertyPair => PropertyDiff(propertyPair))
                            .Where(diff => !Equals(diff.NewValue, diff.OldValue))
                            .ToArray();
            }
            else
            {
                diffPairs = propertyPairsAll
                            .Where(propertyPair => !EqualsForDiff(propertyPair.PropertyValue1?.ValueUntyped, propertyPair.PropertyValue2?.ValueUntyped))
                            .Select(propertyPair => PropertyDiff(propertyPair))
                            .ToArray();
            }

            PropertyDiff PropertyDiff((IPropertyValue? PropertyValue1, IPropertyValue? PropertyValue2) valueTuple)
            {
                string propertyName = (valueTuple.PropertyValue1?.PropertyUntyped.Name ?? valueTuple.PropertyValue2?.PropertyUntyped.Name) !;

                return(new PropertyDiff(
                           propertyName,
                           valueTuple.PropertyValue1 != null ? renderValue(valueTuple.PropertyValue1) : null,
                           valueTuple.PropertyValue2 != null? renderValue(valueTuple.PropertyValue2) : null));
            }

            return(new ObjectDiff(diffPairs));
        }
        /// <inheritdoc />
        public override bool TryGetMember(GetMemberBinder binder, [MaybeNullWhen(true)] out object result)
        {
            var propertyValue = _propertyContainer.GetPropertyValueUntyped(Search
                                                                           .ByNameOrAlias(binder.Name, _ignoreCase)
                                                                           .SearchInParent(_searchInParent)
                                                                           .ReturnNull());

            if (propertyValue != null)
            {
                result = propertyValue.ValueUntyped;
                return(true);
            }

            // returns null if property was not found.
            result = null;
            return(true);
        }