/// <summary>
        /// Creates a surrogate of the function delegate, which enables double dispatch in the concrete type of its target
        /// </summary>
        public static Func <T, TResult> CreateSurrogate <T, TResult>(Func <T, TResult> function, T prototype, Func <TResult> orElse, TResult defaultResult)
        {
            function = function ?? throw new ArgumentNullException(nameof(function));
            var target     = function.Target ?? throw new ArgumentException("must be bound", nameof(function));
            var dispatch   = new DoubleDispatchObject(target);
            var methodName = function.GetMethodInfo().Name;
            Func <T, TResult> surrogate =
                arg =>
                dispatch.Via(methodName, arg, orElse, defaultResult);

            return(surrogate);
        }
        /// <summary>
        /// Creates a surrogate of the action delegate, which enables double dispatch in the concrete type of its target
        /// </summary>
        public static Action <T> CreateSurrogate <T>(Action <T> action, T prototype, Action orElse)
        {
            action = action ?? throw new ArgumentNullException(nameof(action));
            var        target     = action.Target ?? throw new ArgumentException("must be bound", nameof(action));
            var        dispatch   = new DoubleDispatchObject(target);
            var        methodName = action.GetMethodInfo().Name;
            Action <T> surrogate  =
                arg =>
                dispatch.Via(methodName, arg, orElse);

            return(surrogate);
        }