c# - una - Manejo controlado de excepciones en invocaciones dinámicas con números variables de parámetros
que es una excepción en c sharp (1)
En un hilo resuelto ayer , @hvd me mostró cómo obtener "control" sobre el manejo de excepciones mediante .Invocar cuando se trata con delegados de tipo desconocido (un problema visto en bibliotecas como Isis2 , donde el usuario final proporciona controladores de eventos polimórficos y la biblioteca escribe coincidencias para decidir a qué llamar). La sugerencia de HVD giró en torno a saber cuántos argumentos recibió el manejador de llamadas ascendentes y luego usar esa información para construir un tipo genérico del tipo correcto, lo que le permitió construir un objeto dinámico e invocarlo. La secuencia arrojó un control total sobre el manejo de excepciones.
El núcleo de su sugerencia fue que Isis2 podría considerar hacer upcalls de esta manera:
MethodInfo mi = typeof(Program).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic);
Delegate del = Delegate.CreateDelegate(typeof(Action<,>).MakeGenericType(mi.GetParameters().Select(p => p.ParameterType).ToArray()), mi);
((dynamic)del).Invoke(arg0, arg1);
Aquí está mi pregunta: ¿Alguien puede sugerir una manera de hacer lo mismo que funciona para una cantidad arbitraria de argumentos? Claramente puedo hacer una declaración de cambio y escribir un código para el caso de 1 arg, 2, etc. ¿Pero hay una manera de hacerlo donde mi.GetParameters (). Length nos dice cuántos argumentos?
Como un resumen de cápsula para aquellos que no desean hacer clic en el enlace, el problema central es este: cuando se realizan este tipo de llamadas ascendentes dinámicas, el usuario final (que registró el método que se está llamando) puede emitir una excepción debido a errores. Resulta que cuando no se ejecuta en Visual Studio, cuando se ejecuta directamente en el CLR, C # .Invoke detectará y volverá a generar excepciones, presentándolas como excepciones internas dentro de InvocationTargetException. Esto desenrolla la pila y hace que el usuario perciba que el error ha sido algún tipo de problema con el código que llamó a .Invoke (por ejemplo, con mi código). Esta es la razón por la cual el manual de referencia de C # argumenta que la captura / reintroducción es una práctica deficiente de codificación: uno solo debe detectar excepciones que uno planea manejar ...
hvd explicó que esto era básicamente porque. Invoke no tenía ni idea del número o tipos de los argumentos y en ese modo, aparentemente, atrapa y vuelve a lanzar excepciones por alguna razón. Su solución, esencialmente, fija el número de argumentos (el genérico en el ejemplo: Acción <,>) y esto aparentemente es suficiente para que .Invoke no realice una "captura universal". Pero para usar su ejemplo para código arbitrario, necesito un caso para cada número posible de parámetros. Doable (después de todo, ¿quién querría más de 16?) ¡Pero feo!
De ahí el desafío de hoy: mejorar ese código para que con un fragmento de 3 líneas similar de C # funcione sin importar cuántos parámetros. Por supuesto, el delegado resultante también debe ser invocable, presumiblemente con un vector de objetos, uno por argumento ...
PD: Una razón para el pesimismo: la acción en sí viene en 16 formas, con 1 a 16 argumentos. Así que, para mí, esto sugiere que los desarrolladores de C # no vieron una forma más general de hacerlo, y terminaron con la versión que me correspondería usando una declaración de cambio (y supongo que el cambio tendría casos de 0 a 16 argumentos , ya que necesitaría una Acción <...> con N tipos de argumentos para manejar N argumentos proporcionados por el usuario!)
No quiero dejar esto abierto para siempre, así que hice lo que pude para comprender el problema principal, incluida la descarga del código para. Invocar en Mono. Por lo que puedo decir, el problema original se debe simplemente a una optimización que favorece las invocaciones más rápidas a costa de capturar excepciones de esta manera cuando se realiza una Invocación dinámica en un objeto con un vector de argumento. El código para un delegado dinámico creado utilizando la plantilla genérica simplemente no tiene esta captura en él.
No es una gran respuesta pero no tiene acceso a la implementación .NET de Invoke, aparentemente no será posible dar una mejor.