/// <summary>
        /// Creates a delegate of a specified type which wraps another similar delegate, doing downcasts where necessary.
        /// The created delegate will only work in case the casts are valid.
        /// </summary>
        /// <typeparam name = "TDelegate">The type for the delegate to create.</typeparam>
        /// <param name = "toWrap">The delegate which needs to be wrapped by another delegate.</param>
        /// <returns>A new delegate which wraps the passed delegate, doing downcasts where necessary.</returns>
        public static TDelegate WrapDelegate <TDelegate>(Delegate toWrap)
            where TDelegate : class
        {
            if (!typeof(TDelegate).GetTypeInfo().IsSubclassOf(typeof(MulticastDelegate)))
            {
                throw new ArgumentException("Specified type should be a delegate.");
            }

            MethodInfo toCreateInfo = MethodInfoFromDelegateType(typeof(TDelegate));
            MethodInfo toWrapInfo   = toWrap.GetMethodInfo();

            // Create delegate original and converted parameters.
            // TODO: In the unlikely event that someone would create a delegate with a Closure argument, the following logic will fail. Add precondition to check this?
            IEnumerable <Type> toCreateArguments = toCreateInfo.GetParameters().Select(d => d.ParameterType);

            ParameterInfo[]    test            = toWrapInfo.GetParameters();
            IEnumerable <Type> toWrapArguments = toWrapInfo.GetParameters()
                                                 // Closure argument isn't an actual argument, but added by the compiler for dynamically generated methods (expression trees).
                                                 .SkipWhile(p => p.ParameterType.FullName == "System.Runtime.CompilerServices.Closure")
                                                 .Select(p => p.ParameterType);
            ParameterConversionExpressions parameterExpressions = CreateParameterConversionExpressions(toCreateArguments, toWrapArguments);

            // Create call to wrapped delegate.
            Expression delegateCall = Expression.Invoke(
                Expression.Constant(toWrap),
                parameterExpressions.ConvertedParameters);

            return(Expression.Lambda <TDelegate>(
                       ConvertOrWrapDelegate(delegateCall, toCreateInfo.ReturnType),
                       parameterExpressions.OriginalParameters
                       ).Compile());
        }
        /// <summary>
        /// Creates a delegate of a specified type that represents a method which can be executed on an instance passed as parameter.
        /// </summary>
        /// <typeparam name = "TDelegate">
        /// The type for the delegate. This delegate needs at least one (first) type parameter denoting the type of the instance
        /// which will be passed.
        /// E.g., Action&lt;ExampleObject, object&gt;,
        /// where ExampleObject denotes the instance type and object denotes the desired type of the first parameter of the method.
        /// </typeparam>
        /// <param name = "method">The MethodInfo describing the method of the instance type.</param>
        /// <param name = "options">Options which specify what type of delegate should be created.</param>
        public static TDelegate CreateOpenInstanceDelegate <TDelegate>(
            MethodInfo method,
            CreateOptions options = CreateOptions.None)
            where TDelegate : class
        {
            if (method.IsStatic)
            {
                throw new ArgumentException("Since the delegate expects an instance, the method cannot be static.", nameof(method));
            }

            switch (options)
            {
            case CreateOptions.None:
                // Ordinary delegate creation, maintaining variance safety.
                return(method.CreateDelegate(typeof(TDelegate)) as TDelegate);

            case CreateOptions.Downcasting:
            {
                MethodInfo      delegateInfo       = MethodInfoFromDelegateType(typeof(TDelegate));
                ParameterInfo[] delegateParameters = delegateInfo.GetParameters();

                // Convert instance type when necessary.
                Type delegateInstanceType             = delegateParameters.Select(p => p.ParameterType).First();
                Type methodInstanceType               = method.DeclaringType;
                ParameterExpression instance          = Expression.Parameter(delegateInstanceType);
                Expression          convertedInstance = ConvertOrWrapDelegate(instance, methodInstanceType);

                // Create delegate original and converted arguments.
                IEnumerable <Type>             delegateTypes = delegateParameters.Select(d => d.ParameterType).Skip(1);
                IEnumerable <Type>             methodTypes   = method.GetParameters().Select(m => m.ParameterType);
                ParameterConversionExpressions delegateParameterExpressions = CreateParameterConversionExpressions(delegateTypes, methodTypes);

                // Create method call.
                Expression methodCall = Expression.Call(
                    convertedInstance,
                    method,
                    delegateParameterExpressions.ConvertedParameters);

                return(Expression.Lambda <TDelegate>(
                           ConvertOrWrapDelegate(methodCall, delegateInfo.ReturnType),                                // Convert return type when necessary.
                           new[] { instance }.Concat(delegateParameterExpressions.OriginalParameters)
                           ).Compile());
            }

            default:
                throw new NotSupportedException();
            }
        }
        /// <summary>
        /// Creates a delegate of a specified type that represents the specified static or instance method,
        /// with the specified first argument.
        /// </summary>
        /// <typeparam name = "TDelegate">The type for the delegate.</typeparam>
        /// <param name = "method">The MethodInfo describing the static or instance method the delegate is to represent.</param>
        /// <param name = "instance">When method is an instance method, the instance to call this method on. Null for static methods.</param>
        /// <param name = "options">Options which specify what type of delegate should be created.</param>
        public static TDelegate CreateDelegate <TDelegate>(
            MethodInfo method,
            object instance       = null,
            CreateOptions options = CreateOptions.None)
            where TDelegate : class
        {
            switch (options)
            {
            case CreateOptions.None:
                // Ordinary delegate creation, maintaining variance safety.
                return(method.CreateDelegate(typeof(TDelegate), instance) as TDelegate);

            case CreateOptions.Downcasting:
            {
                MethodInfo delegateInfo = MethodInfoFromDelegateType(typeof(TDelegate));

                // Create delegate original and converted arguments.
                IEnumerable <Type>             delegateTypes = delegateInfo.GetParameters().Select(d => d.ParameterType);
                IEnumerable <Type>             methodTypes   = method.GetParameters().Select(p => p.ParameterType);
                ParameterConversionExpressions delegateParameterExpressions = CreateParameterConversionExpressions(delegateTypes, methodTypes);

                // Create method call.
                Expression methodCall = Expression.Call(
                    instance == null ? null : Expression.Constant(instance),
                    method,
                    delegateParameterExpressions.ConvertedParameters);

                return(Expression.Lambda <TDelegate>(
                           ConvertOrWrapDelegate(methodCall, delegateInfo.ReturnType),                                // Convert return type when necessary.
                           delegateParameterExpressions.OriginalParameters
                           ).Compile());
            }

            default:
                throw new NotSupportedException();
            }
        }