/// <summary>
            /// Subscribes to the specified event.
            /// </summary>
            /// <param name="target">The object that publishes the event.</param>
            /// <param name="eventInfo">The event to which we subscribe.</param>
            public EventArgsTask(object target, EventInfo eventInfo)
            {
                _tcs       = new TaskCompletionSource <TEventArgs>();
                _target    = target;
                _eventInfo = eventInfo;
                var eventCompletedMethod = ReflectionShim.GetMethod(GetType(), "EventCompleted");

                _subscription = ReflectionShim.CreateDelegate(eventInfo.EventHandlerType, this, eventCompletedMethod);
                eventInfo.AddEventHandler(target, _subscription);
            }
        /// <summary>
        /// Gets a task that will complete the next time an event is raised. The event type must follow the standard <c>void EventHandlerType(object, TResult)</c> pattern. Be mindful of race conditions (i.e., if the event is raised immediately before this method is called, your task may never complete).
        /// </summary>
        /// <param name="target">The object that publishes the event. May not be <c>null</c>.</param>
        /// <param name="eventName">The name of the event. May not be <c>null</c>.</param>
        /// <returns>The event args.</returns>
        public static Task <TResult> FromEvent(object target, string eventName)
        {
            var eventInfo = ReflectionShim.GetEvent(target.GetType(), eventName);

            if (eventInfo == null)
            {
                throw new InvalidOperationException("Could not find event " + eventName + " on type " + target.GetType().FullName);
            }

            return(new EventArgsTask <TResult>(target, eventInfo).Task);
        }
        /// <summary>
        /// Gets a task that will complete the next time an event is raised. The event type must follow the standard <c>void EventHandlerType(object, TResult)</c> pattern. Be mindful of race conditions (i.e., if the event is raised immediately before this method is called, your task may never complete).
        /// </summary>
        /// <param name="target">The object that publishes the event.</param>
        /// <returns>The event args.</returns>
        public static Task <TResult> FromEvent(object target)
        {
            // Try to look up an event that has the same name as the TResult type (stripping the trailing "EventArgs").
            var type       = target.GetType();
            var resultType = typeof(TResult);
            var resultName = resultType.Name;

            if (resultName.EndsWith("EventArgs", StringComparison.Ordinal))
            {
                var eventInfo = ReflectionShim.GetEvent(type, resultName.Remove(resultName.Length - 9));
                if (eventInfo != null)
                {
                    return(new EventArgsTask <TResult>(target, eventInfo).Task);
                }
            }

            // Try to match to any event with the correct signature.
            EventInfo match = null;

            foreach (var eventInfo in ReflectionShim.GetEvents(type))
            {
                var invoke = ReflectionShim.GetMethod(eventInfo.EventHandlerType, "Invoke");
                if (invoke.ReturnType != typeof(void))
                {
                    continue;
                }
                var parameters = invoke.GetParameters();
                if (parameters.Length != 2 || parameters[0].ParameterType != typeof(object) || parameters[1].ParameterType != resultType)
                {
                    continue;
                }

                if (match != null)
                {
                    throw new InvalidOperationException("Found multiple matching events on type " + target.GetType().FullName);
                }
                match = eventInfo;
            }

            if (match == null)
            {
                throw new InvalidOperationException("Could not find a matching event on type " + target.GetType().FullName);
            }
            return(new EventArgsTask <TResult>(target, match).Task);
        }