2009-11-05 8 views
9

Ich dachte, dass eine Delegat-Instanz mit einer Funktionsinstanz austauschbar ist.Was ist der Unterschied zwischen einer Delegat-Instanz und einem Methodenzeiger?

Nehmen Sie den folgenden Code ein:

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

Beide Möglichkeiten des Aufrufs es gleichwertig zu sein scheinen, und wenn Sie nur mit C#, werden Sie nie den Unterschied sehen (zumindest habe ich nicht bis zu dieser Punkt). Allerdings wurde ich kürzlich unmanaged Code, der in diesen verwalteten Code zurückgerufen wurde, sie werden anders behandelt. Zum Beispiel, in einem Szenario, ich bekomme den Fehler "Ein Rückruf wurde auf einem Garbage Collected Delegate gemacht", wenn ich die Funktion direkt als Rückruf verwenden (obwohl meine Objektinstanz gehalten wird). Die Verwendung der "Delegate-Instanz" behebt das Problem.

Gibt es jemanden da draußen, der weiß, was der Unterschied ist?

+1

Wie sieht der il aus? Gibt es einen Hinweis "unter der Haube", dass sie normalerweise anders behandelt würden? –

Antwort

13

Terminologiekorrektur: Anstelle des Methodenzeigers ist der am besten geeignete Begriff die Methodengruppe.

In Bezug auf die Funktionalität sind die beiden Aussagen gleichwertig. Das heißt, sie produzieren fast die gleiche IL. Der Unterschied besteht darin, wo der Delegatenwert gespeichert ist.

Im ersten Fall übergeben Sie die Methodengruppe Add to MethodThatTakesAdd direkt. Das verursacht einen temporären Delegate-Wert, der erstellt und dann an MethodThatTakesAdd übergeben wird. Dieser Delegatwert unterliegt der Speicherbereinigung in dem Moment, in dem MethodThatTakesAdd zurückgegeben wird, da der Wert nicht gespeichert wird.

Im zweiten Fall haben Sie den Delegaten einem Feld auf der äußeren Instanz zugewiesen. Dies wird in der Regel erhöhen Sie die Lebensdauer des Delegaten und damit die Möglichkeit, es ist Müll während Ihres pinvoke Anruf gesammelt.

+0

Das klingt sehr plausibel und würde erklären, was ich sehe! –

+0

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx erklärt es in "Reverse P/Invoke und Delegate Lifetime" Abschnitt –

+0

@ JaredPar verstehe ich nicht.Ist dies 'AddDelegate DelegateInstance;' ist eine Delegate ** Instanz ** ?? es ist nur eine Variable vom Typ 'AddDelegate'. Es erscheint logisch, dass eine Delegate ** -Instanz ** die ** rechte Seite ** von "myDel + = new MYDEL (myMethod)" - oder "myDel + = myMethod;" (kurz) ist .... .Liege ich falsch ? –

0

Während Delegierte auch Funktionalität in C# als Funktionszeiger in C oder C++ bereitstellen, gibt es signifikante Unterschiede. Der Schlüssel unter diesen ist, dass ein Delegat eine Klasse ist, kein Zeiger.

Kurz gesagt: Wenn Sie einen Delegaten an einen Zeiger übergeben, erhalten Sie keinen Verweis auf eine Funktion oder Methode und können daher nicht dazu verwendet werden, eine Methode über einen Verweis aus nicht verwaltetem Code aufzurufen.

4

Delegaten sind Klassen, die aufrufbar sind und ähnliche Verhalten Funktion Zeiger haben. Die Delegierte speichert intern die Adresse der aufzurufenden Funktion (d. H. Den Funktionszeiger), stellt aber auch andere Funktionen bereit, wie z. B. Mehrfach-Casting und Speichern einer Aufrufliste; Sie können im Wesentlichen viele Funktionen derselben Signatur mit einer Delegat-Instanz wie folgt aufrufen.

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

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

Bezüglich Ihrer Anmerkung über die Gleichwertigkeit von MethodThatTakesAdd(Add) und MethodThatTakesAdd(DelegateInstance), wenn man sich der MSIL aussieht, dass die C# Compiler für die Linie erzeugt MethodThatTakesAdd(Add), werden Sie feststellen, dass der Compiler einen Delegaten und das Einwickeln der Add() Methode schafft für dich.