2009-11-05 22 views
9

Pensé que una instancia de delegado era intercambiable con una instancia de función.¿Cuál es la diferencia entre una instancia de delegado y un puntero de método?

Tome el siguiente código:

delegate int AddDelegate(int a, int b); 

AddDelegate DelegateInstance; 

public void DoStuff() 
{ 
    //I can call this without a delegate "instance": 
    MethodThatTakesAdd(Add); 

    //I can also call it WITH a delegate "instance" 
    DelegateInstance = Add; 
    MethodThatTakesAdd(DelegateInstance); 
} 

public int Add(int a, int b) 
{ 
    return a + b; 
} 

public void MethodThatTakesAdd(AddDelegate addFunction) 
{ 
    Console.WriteLine(addFunction(1, 2).ToString()); 
} 

Ambas formas de llamarlo parecen ser equivalentes, y si solamente va a utilizar C#, que nunca ver la diferencia (al menos no lo he hecho hasta este punto). Sin embargo, recientemente recibí un código no administrado que llamaba nuevamente a este código administrado, se tratan de manera diferente. Por ejemplo, en un escenario, obtengo el error "Se realizó una devolución de llamada en un delegado recolectado basura" si uso la función directamente como una devolución de llamada (aunque mi instancia de objeto se mantiene). El uso de la "instancia de delegado" soluciona el problema.

¿Hay alguien por ahí que sepa cuál es la diferencia?

+1

¿Qué aspecto tiene? ¿Hay alguna indicación "bajo el capó" de que normalmente serían tratados de manera diferente? –

Respuesta

13

Corrección de terminología: en lugar de puntero de método, el término más apropiado es grupo de métodos.

En términos de funcionalidad, las dos declaraciones son equivalentes. Es decir que producen casi la misma IL. La diferencia es donde se almacena el valor del delegado.

En el primer caso, pase directamente al grupo de métodos Agregar al métodoThatTakesAdd. Esto hace que se cree un valor de delegado temporal y luego se pasa a MethodThatTakesAdd. Este valor de delegado está sujeto a la recolección de basura en el momento en que retorna MethodThatTakesAdd ya que no almacena el valor.

En el segundo caso, asignó el delegado a un campo en la instancia externa. Esto normalmente aumenta la vida útil del delegado y, por lo tanto, reduce la probabilidad de que se recolecte basura durante su llamada pinvoke.

+0

¡Eso suena muy plausible, y explicaría lo que estoy viendo! –

+0

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx lo explica en la sección "Invertir P/invocar y delegar vida" –

+0

@JaredPar No entiendo.hace esto 'AddDelegate DelegateInstance;' es un delegado ** instancia ** ?? es solo una variable de tipo 'AddDelegate'. parece lógico que un delegado ** instancia ** sea el ** lado derecho ** de 'myDel + = new MYDEL (myMethod);' - o 'myDel + = myMethod;' (en resumen) .... .Me equivoco ? –

0

Mientras que los delegados también proporcionan funcionalidad en C# como punteros a funciones en C o C++, existen diferencias significativas. La clave entre estos es que un delegado es una clase, no un puntero.

En resumen, al asignar un delegado a un puntero no se le dará una referencia a una función o método, y como tal, no se puede usar para llamar a un método por referencia desde un código no administrado.

4

Los delegados son clases que se pueden llamar, y tienen un comportamiento similar para los punteros de la función. El delegado almacena internamente la dirección de la función para llamar (es decir, el puntero a la función), pero también proporciona otras funcionalidades, como multidifusión y almacenamiento de una lista de invocación; En esencia, puede invocar muchas funciones de la misma firma con una instancia de delegado de la siguiente manera.

public void DoStuff() 
{ 
    DelegateInstance += Add; 
    DelegateInstance += AnotherAdd; 
    DelegateInstance += YetAnotherAdd; 

    // Invoke Add(100, 200), AnotherAdd(100, 200), and YetAnotherAdd(100, 200) 
    DelegateInstance(100, 200); 
} 

En cuanto a su nota sobre la equivalencia de MethodThatTakesAdd(Add) y MethodThatTakesAdd(DelegateInstance), si nos fijamos en el MSIL que el compilador de C# genera para la línea MethodThatTakesAdd(Add), se dará cuenta que el compilador es la creación de un delegado y envolviendo el método Add() para ti.

Cuestiones relacionadas