Instead of Indexed Overloads...

Oct 13, 2009 at 3:30 AM

Perhaps you thought of this, but you could avoid creating a bunch of indexed overloads for all of the BCL's algorithms. Instead, you create a set of methods for converting indexed methods into non-indexed methods.

Below is a simple implementation that achieves this. Note that there is a minor performance cost because of the added level of indirection. I also provided some methods that make up for C# lack of conversion between delegates with the same signature. I have talked to MS and many others in the community and while they all agree it would be nice if matching delegates were interchangeable, they are not willing to change the compiler. I am working on creating a generic means of carrying out these conversions/wrapping using some nifty compile-time tricks.

        #region ToPredicate

        /// <summary>
        /// Creates a Predicate from a Func with the same signature.
        /// </summary>
        /// <typeparam name="T">The type of the argument.</typeparam>
        /// <param name="func">The method to convert to a Predicate.</param>
        /// <returns>The Predicate.</returns>
        public static Predicate<T> ToPredicate<T>(this Func<T, bool> func)
        {
            return value => func(value);
        }

        #endregion

        #region ToComparison

        /// <summary>
        /// Creates a Comparison from a Func with the same signature.
        /// </summary>
        /// <typeparam name="T">The type of the arguments.</typeparam>
        /// <param name="func">The method to convert to a Comparison.</param>
        /// <returns>The Comparison.</returns>
        public static Comparison<T> ToComparison<T>(this Func<T, T, int> func)
        {
            return (value1, value2) => func(value1, value2);
        }

        #endregion

        #region CloseIndex

        /// <summary>
        /// Converts an indexed action into a normal action.
        /// </summary>
        /// <typeparam name="TElement">The type of value acted upon.</typeparam>
        /// <param name="action">The indexed action to wrap.</param>
        /// <returns>An action that redirects to the indexed action.</returns>
        public static Action<TElement> CloseIndex<TElement>(this Action<TElement, int> action)
        {
            return closeIndex<TElement>(action, 0, 1);
        }

        /// <summary>
        /// Converts an indexed action into a normal action.
        /// </summary>
        /// <typeparam name="TElement">The type of value acted upon.</typeparam>
        /// <param name="action">The indexed action to wrap.</param>
        /// <param name="startingIndex">The index to start counting from.</param>
        /// <returns>An action that redirects to the indexed action.</returns>
        public static Action<TElement> CloseIndex<TElement>(this Action<TElement, int> action, int startingIndex)
        {
            return closeIndex<TElement>(action, startingIndex, 1);
        }

        /// <summary>
        /// Converts an indexed action into a normal action.
        /// </summary>
        /// <typeparam name="TElement">The type of value acted upon.</typeparam>
        /// <param name="action">The indexed action to wrap.</param>
        /// <param name="startingIndex">The index to start counting from.</param>
        /// <param name="seed">The amount to change the index by after each call.</param>
        /// <returns>An action that redirects to the indexed action.</returns>
        public static Action<TElement> CloseIndex<TElement>(this Action<TElement, int> action, int startingIndex, int seed)
        {
            return closeIndex<TElement>(action, startingIndex, seed);
        }

        private static Action<TElement> closeIndex<TElement>(Action<TElement, int> action, int index, int seed)
        {
            return value =>
            {
                action(value, index);
                index += seed;
            };
        }

        /// <summary>
        /// Converts an indexed function into a function that doesn't take the index.
        /// </summary>
        /// <typeparam name="TElement">The type of value being checked.</typeparam>
        /// <typeparam name="TResult">The type of the function return value.</typeparam>
        /// <param name="func">The indexed predicate to wrap.</param>
        /// <returns>A predicate that redirects to the indexed predicate.</returns>
        public static Func<TElement, TResult> CloseIndex<TElement, TResult>(this Func<TElement, int, TResult> func)
        {
            return closeIndex<TElement, TResult>(func, 0, 1);
        }

        /// <summary>
        /// Converts an indexed function into a function that doesn't take the index.
        /// </summary>
        /// <typeparam name="TElement">The type of value being checked.</typeparam>
        /// <typeparam name="TResult">The type of the function return value.</typeparam>
        /// <param name="func">The indexed predicate to wrap.</param>
        /// <param name="startingIndex">The index to start counting from.</param>
        /// <returns>A predicate that redirects to the indexed predicate.</returns>
        public static Func<TElement, TResult> CloseIndex<TElement, TResult>(this Func<TElement, int, TResult> func,
            int startingIndex)
        {
            return closeIndex<TElement, TResult>(func, startingIndex, 1);
        }

        /// <summary>
        /// Converts an indexed function into a function that doesn't take the index.
        /// </summary>
        /// <typeparam name="TElement">The type of value being checked.</typeparam>
        /// <typeparam name="TResult">The type of the function return value.</typeparam>
        /// <param name="func">The indexed predicate to wrap.</param>
        /// <param name="startingIndex">The index to start counting from.</param>
        /// <param name="seed">The amount to change the index by after each call.</param>
        /// <returns>A predicate that redirects to the indexed predicate.</returns>
        public static Func<TElement, TResult> CloseIndex<TElement, TResult>(this Func<TElement, int, TResult> func,
            int startingIndex,
            int seed)
        {
            return closeIndex<TElement, TResult>(func, startingIndex, seed);
        }

        private static Func<TElement, TResult> closeIndex<TElement, TResult>(Func<TElement, int, TResult> func,
            int index,
            int seed)
        {
            return value =>
                {
                    TResult result = func(value, index);
                    index += seed;
                    return result;
                };
        }

        #endregion