2009-11-05 4 views
9

Je pensais qu'une instance de délégué était interchangeable avec une instance de fonction.Quelle est la différence entre une instance de délégué et un pointeur de méthode?

Prenez le code suivant:

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()); 
} 

Les deux façons de l'appeler semblent équivalents, et si vous utilisez que C#, vous ne verrez jamais la différence (au moins je n'ai pas jusqu'à ce point). Cependant, j'ai récemment été code non managé qui rappelait dans ce code managé, ils sont traités différemment. Par exemple, dans un scénario, je reçois l'erreur «Un rappel a été effectué sur un délégué collecté par les ordures» si j'utilise la fonction directement en tant que rappel (même si mon instance d'objet est conservée). L'utilisation de "l'instance de délégué" résout le problème.

Y at-il quelqu'un là-bas qui sait quelle est la différence?

+1

À quoi ressemble le il? Y at-il une indication «sous le capot» qu'ils seraient normalement traités différemment? –

Répondre

13

Terminologie Corretion: Au lieu du pointeur de méthode, le terme le plus approprié est groupe de méthodes.

En termes de fonctionnalité, les deux instructions sont équivalentes. C'est qu'ils produisent presque le même IL. La différence est l'endroit où la valeur du délégué est stockée.

Dans le premier cas, vous passez directement le groupe de méthodes Add to MethodThatTakesAdd. Cela provoque la création d'une valeur de délégué temporaire, puis transmise à MethodThatTakesAdd. Cette valeur de délégué est soumise à la récupération de place au moment où la méthode MethodThatTakesAdd est renvoyée car elle ne stocke pas la valeur.

Dans le second cas, vous avez affecté le délégué à un champ de l'instance externe. Cela généralement augmenter la durée de vie du délégué et réduire ainsi la chance qu'il est collecté lors de votre appel pinvoke.

+0

Cela semble très plausible, et pourrait expliquer ce que je vois! –

+0

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx l'explique dans la section "Reverse P/Invoke et Delegate Lifetime" –

+0

@JaredPar Je ne comprends pas.est-ce que AddDelegate DelegateInstance; 'est une instance de délégué ** **? c'est juste une variable de type 'AddDelegate'. il semble logique qu'un délégué ** instance ** soit le ** côté droit ** de 'myDel + = nouveau MYDEL (myMethod),' - ou 'myDel + = myMethod;' (en bref) .... .ai-je tort ? –

0

Alors que les délégués fournissent des fonctionnalités synonymes en C# comme des pointeurs de fonction en C ou C++, il existe des différences significatives. La clé parmi ceux-ci est que un délégué est une classe, pas un pointeur. En résumé, l'envoi d'un délégué à un pointeur ne va pas vous donner de référence à une fonction ou une méthode, et en tant que tel, il ne peut pas être utilisé pour appeler une méthode par référence à partir d'un code non managé.

4

Les délégués sont des classes qui sont appelables et ont un comportement similaire aux pointeurs de fonction. Le délégué stocke en interne l'adresse de la fonction à appeler (c'est-à-dire le pointeur de fonction), mais fournit également d'autres fonctionnalités telles que la multi-diffusion et le stockage d'une liste d'invocation; Vous pouvez essentiellement appeler de nombreuses fonctions de la même signature avec une instance de délégué comme suit.

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

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

En ce qui concerne votre note sur l'équivalence des MethodThatTakesAdd(Add) et MethodThatTakesAdd(DelegateInstance), si vous regardez le MSIL que le compilateur C# génère pour la ligne MethodThatTakesAdd(Add), vous remarquerez que le compilateur crée un délégué et enveloppant la méthode Add() pour toi.

Questions connexes