2009-02-09 15 views
7

¿Cómo trabajan los delegados en C# detrás de escena y cómo se pueden usar de manera eficiente?¿Cómo trabajan los delegados (en el fondo)?

EDIT: Sé cómo funcionan en la superficie (son básicamente punteros de función y permiten invocar métodos de devolución de llamada con ciertas firmas usando su dirección). Lo que necesito saber es cómo el CLR realmente los implementa internamente. ¿Qué sucede exactamente detrás de las escenas cuando defines un delegado y cuando invocas un método de devolución de llamada usando el objeto delegado?

Respuesta

4

La primera parte de la pregunta es relativamente fácil: los delegados almacenan una lista de punteros de función. Si invoca a un delegado, llama a todos los punteros de función en esa lista interna. Agregar y eliminar un receptor (a través de Delegate.Combine y Delegate.Remove) equivale a agregar y quitar de esa lista.

Para obtener más información de bajo nivel, consulte ECMA-335 (el estándar CLI), sección II.14.5 (punteros de método) y II.14.6 (Delegados). En particular, tenga en cuenta que un delegado consta de un puntero de instancia (del tipo System.Object) y un puntero de método (del tipo System.IntPtr). Se puede obtener un puntero de método (en CIL) a través de las instrucciones ldftn o ldvirtftn (para llamadas a funciones virtuales).

Estas dos piezas de información identifican cualquier método.

¿cómo se pueden usar de manera eficiente?

¿Qué quieres decir con eso? ¿Conoces los eventos o tu pregunta es más especializada?

+0

serio? Almacenan punteros? Tenía la impresión de que los eventos lo hicieron, y que los delegados eran como un tipo de función fuertemente tipada. (Pero podría estar confundiendo las cosas allí.) – scraimer

+0

scraimer: Los eventos usan delegados internamente. Además de eso, ofrecen una interfaz para un acceso más fácil. Debo aclarar esto. –

+0

Sé todo sobre los indicadores de función, pero lo que me gustaría saber es cómo los implementa realmente el CLR y cómo puedo aprovechar el conocimiento sobre su implementación para utilizarlos de manera más eficiente en mi código – Lonzo

5

Re eficiencia - no está claro lo que quiere decir, pero se pueden utilizar para lograr eficiencia, evitando la reflexión costosa. Por ejemplo, al usar Delegate.CreateDelegate para crear un delegado preestablecido (escrito a máquina) en un método dinámico/buscado, en lugar de usar el (más lento) MethodInfo.Invoke.

Para obtener un ejemplo trivial (acceda al patrón estático T Parse(string) para un tipo), consulte a continuación. Tenga en cuenta que solo utiliza la reflexión una vez (por tipo), en lugar de muchas veces. Esto debería superar el rendimiento de reflexión o bien típico de uso TypeConverter:

using System; 
using System.Reflection; 
static class Program { // formatted for space 
    static void Main() { 
     // do this in a loop to see benefit... 
     int i = Test<int>.Parse("123"); 
     float f = Test<float>.Parse("123.45"); 
    } 
} 
static class Test<T> { 
    public static T Parse(string text) { return parse(text); } 
    static readonly Func<string, T> parse; 
    static Test() { 
     try { 
      MethodInfo method = typeof(T).GetMethod("Parse", 
       BindingFlags.Public | BindingFlags.Static, 
       null, new Type[] { typeof(string) }, null); 
      parse = (Func<string, T>) Delegate.CreateDelegate(
       typeof(Func<string, T>), method); 
     } catch (Exception ex) { 
      string msg = ex.Message; 
      parse = delegate { throw new NotSupportedException(msg); }; 
     } 
    } 
} 
3

delegados en C# son listas de punteros a métodos. Es decir. almacenan referencias al código, y puede invocar los métodos a través de los punteros. Esto es útil en muchos casos. El ejemplo común es para los manejadores de eventos donde los delegados se utilizan para implementar un patrón de editor/suscriptor.

1

El compilador de C# genera una clase completa cuando crea un delegado. Esta clase contiene una lista de referencias de funciones, como mencionó Konrad. Lo bueno de los delegados es que le proporcionan una manera fácil de ejecutar la tarea de forma asíncrona con la devolución de llamadas de notificación. Lo que esto significa es que se le puede notificar cuando finalice su operación en segundo plano. Threadpool no proporciona esta característica. Los delegados son un tema amplio y encuentro que los libros de Jeff Richter (CLR vía C#) y Albahari (C# 3) son de particular ayuda.

0

C# delegados son objetos (consulte la clase System.Delegate) que encapsulan una referencia a un objeto y un puntero de método. También pueden tener una referencia nula al objeto para representar una llamada a un método estático.

Cuando se invoca el delegado con argumentos, el delegado de la artesanía en una llamada al método que se hace referencia en el objeto referenciado con argumentos especificados.

Un método Invoke delegado compilado se realiza directamente por el tiempo de ejecución (como visible con reflector):

[MethodImpl(0, MethodCodeType=MethodCodeType.Runtime)] 
public virtual void Invoke(T obj); 

El tiempo de ejecución utilizar toda la información internaly para compilar una llamada al método estándar para el método de referencia.

4

Al definir su delegado

internal delegate void Feedback(Int32 value); 

el compilador en realidad define una clase completa que se ve algo como esto :

internal class Feedback : System.MulticastDelegate { 
    // Constructor 
    public Feedback(Object object, IntPtr method); 

    // Method with same prototype as specified by the source code 
    public virtual void Invoke(Int32 value); 

    // Methods allowing the callback to be called asynchronously 
    public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object object); 

    public virtual void EndInvoke(IAsyncResult result); 
} 

Fuente: Jeffrey Richter - CLR via C#, capítulo 17

Cuestiones relacionadas