/// <summary>
        /// Uses reflection to dynamically invoke a method,
        /// throwing an exception if it is not
        /// implemented on the target object.
        /// </summary>
        /// <param name="obj">
        /// Object containing method.
        /// </param>
        /// <param name="info">
        /// MethodInfo for the method.
        /// </param>
        /// <param name="parameters">
        /// Parameters to pass to method.
        /// </param>
        public static object CallMethod(object obj, MethodInfo info, params object[] parameters)
        {
            DynamicMethodHandle mh = GetCachedMethod(obj, info, parameters);

            if (mh == null || mh.DynamicMethod == null)
            {
                throw new NotImplementedException(info.Name + " not implemented.");
            }
            return(CallMethod(obj, mh, parameters));
        }
        /// <summary>
        /// Uses reflection to dynamically invoke a method
        /// if that method is implemented on the target object.
        /// </summary>
        /// <param name="obj">
        /// Object containing method.
        /// </param>
        /// <param name="method">
        /// Name of the method.
        /// </param>
        /// <param name="parameters">
        /// Parameters to pass to method.
        /// </param>
        public static object CallMethodIfImplemented(object obj, string method, params object[] parameters)
        {
            DynamicMethodHandle mh = GetCachedMethod(obj, method, parameters);

            if (mh == null || mh.DynamicMethod == null)
            {
                return(null);
            }
            return(CallMethod(obj, mh, parameters));
        }
        /// <summary>
        /// Uses reflection to dynamically invoke a method,
        /// throwing an exception if it is not implemented
        /// on the target object.
        /// </summary>
        /// <param name="obj">
        /// Object containing method.
        /// </param>
        /// <param name="methodHandle">
        /// MethodHandle for the method.
        /// </param>
        /// <param name="parameters">
        /// Parameters to pass to method.
        /// </param>
        private static object CallMethod(object obj, DynamicMethodHandle methodHandle, params object[] parameters)
        {
            object result = null;
            DynamicMemberMethod method = methodHandle.DynamicMethod;

            object[] inParams = null;
            if (parameters == null)
            {
                inParams = new object[] { null }
            }
            ;
            else
            {
                inParams = parameters;
            }

            if (methodHandle.HasFinalArrayParam)
            {
                int pCount = methodHandle.MethodParamsLength;
                // last param is a param array or only param is an array
                int extras = inParams.Length - (pCount - 1);

                // 1 or more params go in the param array
                // copy extras into an array
                object[] extraArray = GetExtrasArray(extras, methodHandle.FinalArrayElementType);
                Array.Copy(inParams, extraArray, extras);

                // copy items into new array
                var paramList = new object[pCount];
                for (int pos = 0; pos <= pCount - 2; pos++)
                {
                    paramList[pos] = parameters[pos];
                }
                paramList[paramList.Length - 1] = extraArray;

                // use new array
                inParams = paramList;
            }
            try
            {
                result = methodHandle.DynamicMethod(obj, inParams);
            }
            catch (Exception ex)
            {
                throw new CallMethodException(methodHandle.MethodName + " method call failed.", ex);
            }
            return(result);
        }
        private static DynamicMethodHandle GetCachedMethod(object obj, MethodInfo info, params object[] parameters)
        {
            var key = new MethodCacheKey(obj.GetType().FullName, info.Name, GetParameterTypes(parameters));
            DynamicMethodHandle mh = null;
            if (_methodCache.TryGetValue(key, out mh))
                return mh;

            lock (_methodCache)
            {
                if (!_methodCache.TryGetValue(key, out mh))
                {
                    mh = new DynamicMethodHandle(info, parameters);
                    _methodCache.Add(key, mh);
                }
            }
            return mh;
        }
        private static DynamicMethodHandle GetCachedMethod(object obj, MethodInfo info, params object[] parameters)
        {
            var key = new MethodCacheKey(obj.GetType().FullName, info.Name, GetParameterTypes(parameters));
            DynamicMethodHandle mh = null;

            if (_methodCache.TryGetValue(key, out mh))
            {
                return(mh);
            }

            lock (_methodCache)
            {
                if (!_methodCache.TryGetValue(key, out mh))
                {
                    mh = new DynamicMethodHandle(info, parameters);
                    _methodCache.Add(key, mh);
                }
            }
            return(mh);
        }
        /// <summary>
        /// Uses reflection to dynamically invoke a method,
        /// throwing an exception if it is not implemented
        /// on the target object.
        /// </summary>
        /// <param name="obj">
        /// Object containing method.
        /// </param>
        /// <param name="methodHandle">
        /// MethodHandle for the method.
        /// </param>
        /// <param name="parameters">
        /// Parameters to pass to method.
        /// </param>
        private static object CallMethod(object obj, DynamicMethodHandle methodHandle, params object[] parameters)
        {
            object result = null;
            DynamicMemberMethod method = methodHandle.DynamicMethod;

            object[] inParams = null;
            if (parameters == null)
                inParams = new object[] { null };
            else
                inParams = parameters;

            if (methodHandle.HasFinalArrayParam)
            {
                int pCount = methodHandle.MethodParamsLength;
                // last param is a param array or only param is an array
                int extras = inParams.Length - (pCount - 1);

                // 1 or more params go in the param array
                // copy extras into an array
                object[] extraArray = GetExtrasArray(extras, methodHandle.FinalArrayElementType);
                Array.Copy(inParams, extraArray, extras);

                // copy items into new array
                var paramList = new object[pCount];
                for (int pos = 0; pos <= pCount - 2; pos++)
                    paramList[pos] = parameters[pos];
                paramList[paramList.Length - 1] = extraArray;

                // use new array
                inParams = paramList;
            }
            try
            {
                result = methodHandle.DynamicMethod(obj, inParams);
            }
            catch (Exception ex)
            {
                throw new CallMethodException(methodHandle.MethodName + " method call failed.", ex);
            }
            return result;
        }