Why is a SendOrPostCallback delegate dynamically invoked when posted to a WindowsFormsSynchronizationContext?

StackOverflow https://stackoverflow.com/questions/13758769

Pregunta

I'm writing a wrapper that allows me to invoke methods on an arbitrary SynchronizationContext, for example to allow me to queue network related callbacks (e.g. Socket.BeginReceive) to some arbitrary dispatcher/handler, such as a UI thread, or my own implementation designed for serial execution. (to avoid the need to synchronize data structures - locks etc..).

Basically something like this:

..
public void BeginInvoke(MethodCall methodCall)
{
    this.synchronizationContext.Post(this.SynchronizationContextCallback, methodCall);
}

private void SynchronizationContextCallback(Object methodCall)
{
    (methodCall as MethodCall).Invoke();
}
..

It all seemed to work fine with aWindowsFormsSynchronizationContext but when an exception was thrown (A System.Reflection.TargetInvocationException) I realized that that the SendOrPostCallback delegate is being dynamically invoked(?!) here's the relevant code (Microsoft Reference) from the System.Windows.Forms.Control.ThreadMethodEntry class:

private static void InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
{
    // We short-circuit a couple of common cases for speed.
    //
    if (tme.method is EventHandler)
    {
        if (tme.args == null || tme.args.Length < 1)
        {
            ((EventHandler)tme.method)(tme.caller, EventArgs.Empty);
        }
        else if (tme.args.Length < 2)
        {
            ((EventHandler)tme.method)(tme.args[0], EventArgs.Empty);
        }
        else
        {
            ((EventHandler)tme.method)(tme.args[0], (EventArgs)tme.args[1]);
        }
    }
    else if (tme.method is MethodInvoker)
    {
        ((MethodInvoker)tme.method)();
    }
    else if (tme.method is WaitCallback)
    {
        Debug.Assert(tme.args.Length == 1,
                        "Arguments are wrong for WaitCallback");
        ((WaitCallback)tme.method)(tme.args[0]);
    }
    else
    {
        tme.retVal = tme.method.DynamicInvoke(tme.args);
    }
} 

There appears to be no support for SendOrPostCallback delegate but it's interesting to note its signature is completely identical to a WaitCallback! or more generally, an Action<Object>. This leaves me in question, am I doing something wrong here? or is this by design? (both on the language and framework level I mean..). Obviously.. having all the dispatched method calls dynamically invoked would be significantly slower and more difficult to debug? (to the point where I might not even find this a usable solution?). What am I missing here?

¿Fue útil?

Solución

This is inevitable, it can marshal any delegate type. The only way to do that is to use DynamicInvoke(). They did make the effort to pick off some common delegate types you'd use in Winforms programming but that can never be an exhaustive list without losing the benefit of doing so. There is little point in worrying about the cost, this always involves a thread context switch and the latency of the Winforms message loop responding to the PostMessage(). The cost of DynamicInvoke() is small potatoes compared to that.

And yes, you do have to do something to avoid getting slapped by the outer-most exception, the TargetInvocationException isn't useful to anybody. The Winforms Control.Invoke() method does that by re-throwing only the inner-most InnerException. That's not ideal either but better than TIE.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top