0

I've written these extension methods which I'd like to condense into one, since they effectively do the exact same thing. The problem I've run into is that I cannot pass in an HtmlElementEventHandler as an EventHandler parameter.

    public static async Task WaitUntilAsync
        (this Action action, Action<EventHandler> addHandler,
        Action<EventHandler> removeHandler, Predicate<string> pred)
    {
        var t = new TaskCompletionSource<bool>();

        System.EventHandler handler = new System.EventHandler((s, e) => 
            {
                if (pred((s as MyAsyncWebBrowser).DocumentTitle))
                    t.SetResult(true);
            });

        addHandler(handler);

        action();
        await t.Task;

        removeHandler(handler);
    }

    public static async Task WaitUntilAsync
(this Action action, Action<System.Windows.Forms.HtmlElementEventHandler> addHandler,
Action<System.Windows.Forms.HtmlElementEventHandler> removeHandler, Predicate<string> = h => true)
    {
        var t = new TaskCompletionSource<bool>();

        System.Windows.Forms.HtmlElementEventHandler handler = new System.Windows.Forms.HtmlElementEventHandler((s, e) =>
        {
            if (pred)
                t.TrySetResult(true);
        });

        addHandler(handler);

        action();
        await t.Task;

        removeHandler(handler);
    }

Any ideas?

Previous Attempt

    public static async Task WaitUntilAsync<TDelegate>
        (this Action action, Action<TDelegate> addHandler,
        Action<TDelegate> removeHandler, Predicate<string> pred)
    {
        var t = new TaskCompletionSource<bool>();

        TDelegate handler = (s, e) =>
        {
            if (pred((s as MyAsyncWebBrowser).DocumentTitle))
                t.SetResult(true);
        };

        addHandler(handler);

        action();
        await t.Task;

        removeHandler(handler);
    }

Which gives me "Cannot Convert Lambda expression to Type Delegate because it is not a delegate type." at the very beginning.

Darth_Evil
  • 591
  • 1
  • 7
  • 18
  • This question is pretty much a duplicate of the following: http://stackoverflow.com/questions/12865848/general-purpose-fromevent-method – Servy May 05 '14 at 16:20

1 Answers1

2

Try this as your definition. Gotta love generics!

public static async Task WaitUntilAsync<T>(this Action action, Action<T> addHandler, Action<T> removeHandler, Predicate<string> pred) where T : class

Unfortunately, you cannot use delegates as generic constraints like so:

public static async Task WaitUntilAsync<T>(this Action action, Action<T> addHandler, Action<T> removeHandler, Predicate<string> pred) where T : EventHandler

...so you'll have to use reflection to confirm your type and then cast it manually.

Here is a complete example:

  public static async Task WaitUntilAsync<TDelegate>(this Action action, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, Predicate<string> pred) where TDelegate : class
    {
        var delegateType = typeof(TDelegate);

        if (delegateType != typeof(EventHandler) && !delegateType.IsSubclassOf(typeof(EventHandler))) throw new Exception("TDelegate must be EventHandler or a subclass.");

        var t = new TaskCompletionSource<bool>();

        EventHandler realHandler = (sender, args) =>
        {
            //do real event work here
        };

        var handler = Delegate.CreateDelegate(delegateType, realHandler.Method) as TDelegate;

        addHandler(handler);

        action();
        await t.Task;

        removeHandler(handler);
    }
Nathan A
  • 11,059
  • 4
  • 47
  • 63
  • I neglected to mention I tried using generics but it failed... I was getting some strange Delegate cast errors... as I'm sure you just discovered :/... – Darth_Evil May 05 '14 at 15:59
  • What cast errors? You can't constrain generics to delegates, which I mentioned, but there is a workaround to that. – Nathan A May 05 '14 at 16:06
  • If you are talking about casting your handler as T, you can't explicitly cast it, but you don't want to in case it fails. You'd do this: var convertedHandler = handler as T. Then, check if it's null, and if so, throw your exception. Otherwise, use that new value in your addHandler() and removeHandler() calls. Make sure to add 'where T : class' as your generic constraint to make this work. – Nathan A May 05 '14 at 16:15
  • I've updated my Q to include what I tried - pretty much the same as your attempt. I don't need to have a null check. I either pass in an EventHandler or HtmlElementEventHandler. – Darth_Evil May 05 '14 at 16:17
  • 1
    The null check is needed to confirm that your conversion was successful. I'll update my answer to include a complete function definition. – Nathan A May 05 '14 at 16:20
  • 1
    Complete example posted. – Nathan A May 05 '14 at 16:40