public SingleTypePropertyCache(INotifyPropertyChanged singleTypedPropertyNotifier, object singleTypedPropertiesOwner, IEqualityComparer <PropertyType>?propertyValueEqualityComparer)
        {
            singleTypedPropertyNotifier = singleTypedPropertyNotifier
                                          ?? throw new ArgumentNullException(nameof(singleTypedPropertyNotifier));

            singleTypedPropertiesOwner = singleTypedPropertiesOwner
                                         ?? throw new ArgumentNullException(nameof(singleTypedPropertiesOwner));

            propertyValueEqualityComparer = propertyValueEqualityComparer
                                            ?? EqualityComparer <PropertyType> .Default;

            TrackingPropertyType = typeof(PropertyType);
            var propertyTypeInfo = TrackingPropertyType.GetTypeInfo();

            if (propertyTypeInfo.IsValueType)
            {
                propertyComparisonMode = PropertyComparisonMode.ValueType;
            }
            else
            {
                propertyComparisonMode = PropertyComparisonMode.ReferenceType;
            }

            cachedPropertyValues         = new Dictionary <string, PropertyType>();
            CachedPropertyValues         = new ReadOnlyDictionary <string, PropertyType>(cachedPropertyValues);
            VariableInfoDescriptor       = new VariableInfoDescriptor();
            TrackingPropertyDefaultValue = default;
            CanHandleDefaultValue        = true;

            SingleTypedPropertyNotifier = singleTypedPropertyNotifier;
            SingleTypedPropertyNotifier.PropertyChanged += SingleTypePropertyNotifier_PropertyChanged;
            SingleTypedPropertiesOwner     = singleTypedPropertiesOwner;
            SingleTypedPropertiesOwnerType = singleTypedPropertiesOwner.GetType();
            PropertyValueEqualityComparer  = propertyValueEqualityComparer;
        }
        /// <summary>
        /// We calling it, you are forcing a property being tracked.
        /// </summary>
        /// <param name="propertyName"></param>
        public void SingleTypePropertyNotifierPropertyChanged(string propertyName)
        {
            VariableInfoDescriptor.Flags |= BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.GetField;
            var propertyMember = SingleTypedPropertiesOwnerType.GetVariableMember(propertyName, VariableInfoDescriptor);

            // There is no member that can be handled, so we return.
            if (propertyMember == null)
            {
                return;
            }

            var          propertyType = propertyMember.GetVariableType();
            PropertyType typedPropertyValue;
            var          propertyValue = propertyMember.GetValue(SingleTypedPropertiesOwner);

            // We want to assign property value (object) to typed property value.
            if (propertyComparisonMode == PropertyComparisonMode.ValueType && TrackingPropertyType == propertyType ||
                propertyComparisonMode == PropertyComparisonMode.ReferenceType && TrackingPropertyType.IsAssignableFrom(propertyType))
            {
                typedPropertyValue = (PropertyType)propertyValue;
            }
            else
            {
                return; // The type of the property does not meet the requirements, so we skip the property
            }

            var isPropertNameTracked = cachedPropertyValues.ContainsKey(propertyName);

            // We want to abort if tracking property name is untracked and tracking property value is equals default/null.
            if (!isPropertNameTracked &&
                CanHandleDefaultValue &&
                PropertyValueEqualityComparer.Equals(typedPropertyValue !, TrackingPropertyDefaultValue !))
            {
                return;
            }

            void trackPropertyViaArgs(PropertyCachedEventArgs <PropertyType> args)
            {
                cachedPropertyValues.Add(propertyName, args.PropertyValue !);
                OnPropertyCacheAdded(args);
            }

            void trackProperty(PropertyType uncachedProperty)
            {
                var args = new PropertyCachedEventArgs <PropertyType>(propertyName, uncachedProperty);

                trackPropertyViaArgs(args);
            }

            void untrackProperty(PropertyType cachedProperty, bool isRetrack)
            {
                cachedPropertyValues.Remove(propertyName);

                if (isRetrack && CanSkipPropertyRemovedEventInvocationWhenRetracking)
                {
                    return;
                }

                var args = new PropertyCacheRemovedEventArgs <PropertyType>(propertyName, cachedProperty);

                OnPropertyCacheRemoved(args);
            }

            void retrackProperty(PropertyType cachedProperty, PropertyType uncachedProperty)
            {
                untrackProperty(cachedProperty, true);
                var args = new PropertyCachedEventArgs <PropertyType>(propertyName, uncachedProperty, cachedProperty);

                trackPropertyViaArgs(args);
            }

            if (!isPropertNameTracked)
            {
                var args = new PropertyCachingEventArgs <PropertyType>(propertyName, typedPropertyValue);
                OnPropertyCacheAdding(args);

                if (!args.CanTrackProperty)
                {
                    return;
                }

                // Add new subscription
                trackProperty(typedPropertyValue !);
            }
            else
            {
                var cachedProperty = cachedPropertyValues[propertyName];

                if (CanHandleDefaultValue && PropertyValueEqualityComparer.Equals(typedPropertyValue !, TrackingPropertyDefaultValue !))
                {
                    untrackProperty(cachedProperty, false);
                }