¿Se puede crear un delegado de un método de instancia sin especificar la instancia en el momento de la creación? En otras palabras, ¿puedes crear un delegado "estático" que tome como primer parámetro la instancia en la que se debe invocar el método?"Despliegue" de un método de instancia en .NET
Por ejemplo, ¿cómo puedo construir el siguiente delegado utilizando la reflexión?
Func<int, string> = i=>i.ToString();
Soy consciente del hecho de que puedo usar MethodInfo.Invoke, pero esto es más lento, y no comprueba para el tipo-corrección hasta que se llama.
Cuando se tiene la MethodInfo
de un determinado método estático , es posible construir un delegado utilizando Delegate.CreateDelegate(delegateType, methodInfo)
, y todos los parámetros del método estático permanecer libre.
Como señaló Jon Skeet, simplemente puede aplicar lo mismo para hacer un delegado abierto de un método de instancia si el método no es virtual en un tipo de referencia. Decidir qué método utilizar en un método virtual es complicado, por lo que no es tan trivial, y los tipos de valor parecen no funcionar en absoluto.
Para los tipos de valor, CreateDelegate
muestra un comportamiento muy extraño:
var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine(object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?
Calling CreateDelegate
con null
como el objeto de destino emite una excepción de unión si el método de instancia pertenecía a un tipo de valor (esto funciona para los tipos de referencia).
Algunos años de seguimiento después: La diana unido de forma incorrecta, que causó func42(CultureInfo.InvariantCulture);
para volver "-201040128"
en lugar de "42"
en mi ejemplo era una corrupción de memoria que podría haber permitido la ejecución remota de código (cve-2010-1898); esto se solucionó en 2010 en la actualización de seguridad ms10-060. ¡Los marcos actuales imprimen correctamente 42! Eso no facilita la respuesta a esta pregunta, pero explica el comportamiento particularmente extraño en el ejemplo.
Este es uno de esos casos en los que queda claro que C# todavía tiene espacio para crecer como lenguaje funcional. Tratar las funciones como ciudadanos de primera clase todavía no es tan fluida como nos gustaría. ¿Hay alguna forma de explotar las características dinámicas en C# 4 para facilitar este tipo de cosas? – LBushkin
@LBushkin: No lo creo. De hecho, la tipificación dinámica y las lambdas no van muy bien juntas para empezar: el compilador debe saber a qué tipo convertir la expresión lambda en el momento de la compilación. –
I * had * estado probando int.ToString, pero para el uso real supongo que podría prescindir de los métodos virtuales, aunque no sin estructuras. De todos modos, gracias por el aviso, pasé por alto la complejidad de los métodos virtuales, y el mensaje de error no es exactamente informativo ... –