c# – Constrained a new version of ConstrainSetter

This class allows for setting conditions on setting a property. it has success and failure callback, you can modify/transform the input in some way or check/validate it against a known value. This class Takes an input type, creates a private field to represent that type, optionally it can take an expected value and provides a framework for controlling how that field is set, optionally taking action on success or failure, optionally modifying the input, optionally validating the input to see if meets some criteria, and finally with no ModifyAction Function and PreCheck Predicate setup it acts as a normal setter. You can also inherit this class and override Modifyinput and CheckInput.

internal interface IConstrained<T>
{
    T Data { get; set; }
    T ModifyInput(T value);
    bool CheckInput(T value);
}

(Serializable)
internal class Constrained<T> : IConstrained<T>
{
    public Constrained(T expected,
        Action<Constrained<T>, string> failAction,
        Func<Constrained<T>, T,bool> checkBeforeSet,
        Func<Constrained<T>, T, T> modifyBeforeSet,
        Action<Constrained<T>, string> successAfterSet)
    {
        Comparer = expected;
        FailAction += failAction ?? throw new ArgumentNullException(nameof(failAction));
        CheckBeforeSet += checkBeforeSet ?? throw new ArgumentNullException(nameof(checkBeforeSet));
        ModifyBeforeSet += modifyBeforeSet ?? throw new ArgumentNullException(nameof(modifyBeforeSet));
        SuccessAfterSet += successAfterSet ?? throw new ArgumentNullException(nameof(successAfterSet));
    }

    private readonly Action<Constrained<T>, string> FailAction;
    private readonly Action<Constrained<T>, string> SuccessAfterSet;

    #region ControlFlow
    private readonly Func<Constrained<T>, T, bool> CheckBeforeSet;
    private readonly Func<Constrained<T>,T, T> ModifyBeforeSet;
    #endregion

    private T data;
    public T Data { get => data; set => data = Set(value); }
    internal T Comparer { get; }
    public virtual T ModifyInput(T value)
    {
        return ModifyBeforeSet.Invoke(this, value);
    }
    public virtual bool CheckInput(T value)
    {
        return CheckBeforeSet.Invoke(this, value);
    }

    private T Set(T value)
    {
        if (ModifyBeforeSet == default)
        {
            if (CheckBeforeSet != default)
            {
                if (CheckInput(value))
                {
                    if (SuccessAfterSet != default)
                    {
                        SuccessAfterSet(this, "Value successfully set.");
                    }
                    return value;
                }
                if (FailAction != default)
                {
                    FailAction(this, "Value did not pass check before set.");
                }
                return default;
            }
            return value;
        }
        else
        {
            T Local = ModifyInput(value);
            if (CheckBeforeSet != default)
            {
                if (CheckInput(Local))
                {
                    if (SuccessAfterSet != default)
                    {
                        SuccessAfterSet(this, "Value successfully set.");
                    }
                    return Local;
                }
                if (FailAction != default)
                {
                    FailAction(this, "Value did not pass check before set.");
                }
                return default;
            }
            return value;
        }
    }
}

a lame example:

    static void FailedRoutine(Constrained<DateTime> failed, string output)
    {
    }
    static bool CheckBeforeSet(Constrained<DateTime> setter, DateTime date) => date.CompareTo(setter.Comparer) > 0;

    static DateTime ModifyBeforeSet(Constrained<DateTime> setter, DateTime date)
    {
        return date;
    }
    static void SuccessRoutine(Constrained<DateTime> success, string output)
    {

    }
    Constrained<DateTime> setter = new Constrained<DateTime>(DateTime.Now.AddSeconds(10),FailedRoutine, CheckBeforeSet, ModifyBeforeSet, SuccessRoutine);
    var date = DateTime.Now;
    do
    {
        date = DateTime.Now;
        setter.Data = date;
    } while (setter.Data != date);