Search code examples
c#operator-keywordimplicit

Generic type BoundedComponent struct


I want to do a struct for boundedElements like number that has a max and min value. However I'm stuck on how to do this. Here is an example of a property. The setfield method is just a method to set a property and raise a property changed event.

private BoundedComponent<int> nbOfItems = new BoundedComponent<int> (1, int.MaxValue);

public int NbOfItems 
        {
            get => nbOfItems ;
            set => SetField(ref nbOfItems, value);
        }


protected bool SetField<T>(
            ref T field,
            T value,
            [CallerMemberName]
            string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;

            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }

and here is my struct

public struct BoundedComponent<T> where T : struct, IComparable<T>
    {
        private T value;
        private T minValue;
        private T maxValue;

        public BoundedComponent(T minValue, T maxValue)
        {
            this.minValue = minValue;
            this.maxValue = maxValue;
            value = minValue;
        }

        public static implicit operator T(BoundedComponent<T> d)
        {
            return d.value;
        }

        public static implicit operator BoundedComponent<T>(T val)
        {
            value = BoundValue(val);
        }

        private T BoundValue(T value) 
        {
            return value.CompareTo(maxValue) > 0 ? maxValue 
                : value.CompareTo(minValue) < 0 ? minValue : value;
        }
    }

However I cannot do that because the implicit operator is a static method so I don't have access to the min and max values of the bounded element I want to check.

How can I do such a thing ?

I tried the above method and also using a class with a property Value however I cannot use it inside my setfield method since properties cant be passed by reference.


Solution

  • BoundedComponent should not have a conversion from T. An instance of T simply does not have enough information to create an instance of BoundedComponent. Namely, T does not have any information about its minimum and maximum values.

    I would make the BoundValue method actually set value, and rename it to Set.

    public struct BoundedComponent<T> where T : struct, IComparable<T>
    {
        private T value;
        // consider making these readonly
        private readonly T minValue;
        private readonly T maxValue;
    
        public BoundedComponent(T minValue, T maxValue)
        {
            this.minValue = minValue;
            this.maxValue = maxValue;
            value = minValue;
        }
    
        public static implicit operator T(BoundedComponent<T> d)
        {
            return d.value;
        }
    
        public void Set(T value) 
        {
            this.value = value.CompareTo(maxValue) > 0 ? maxValue 
                : value.CompareTo(minValue) < 0 ? minValue : value;
        }
    }
    

    You should create a new SetField method for setting BoundedComponents. As you found out, you cannot use the existing SetField.

    Even if you could, the existing SetField's logic is incorrect for BoundedComponents. Suppose the call SetField(ref nbOfItems, -1) actually works - if nbOfItems was initially 1, this would have unexpectedly raised OnPropertyChanged event.

    You should record the old value, try to call Set, and compare the old value with the current value to see if it changed:

    protected bool SetField<T>(
        ref BoundedComponent<T> field,
        T value,
        [CallerMemberName]
        string propertyName = null)
        where T: struct, IComparable<T>
    {
        T old = field;
        field.Set(value);
        if (EqualityComparer<T>.Default.Equals(old, field))
            return false;
        OnPropertyChanged(propertyName);
        return true;
    }