2009-11-05 3 views
9

Я думал, что экземпляр делегата является взаимозаменяемым с экземпляром функции.В чем разница между экземпляром делегата и указателем метода?

Возьмем следующий код:

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

Оба способы вызова Окажись эквивалентными, и если вы используете только C#, вы никогда не увидите разницу (по крайней мере, я не до эта точка). Тем не менее, я недавно был неуправляемым кодом, который возвращался обратно в этот управляемый код, к ним относятся по-разному. Например, в одном сценарии я получаю сообщение об ошибке «Обратный вызов был сделан на сборнике собранных мусором», если я использую функцию непосредственно в качестве обратного вызова (хотя экземпляр объекта хранится вокруг). Использование «экземпляра делегата» устраняет проблему.

Есть ли кто-то, кто знает, в чем разница?

+1

Как выглядит ИЛ? Есть ли какие-либо указания «под капотом», что к ним обычно относятся иначе? –

ответ

13

Терминология Коррекция: вместо указателя метода более подходящим термином является группа методов.

С точки зрения функциональности два утверждения эквивалентны. То есть они производят почти того же ИЛ. Разница заключается в том, где хранится значение делегата.

В первом случае вы передаете группу методов Add to MethodThatTakesAdd напрямую. Это приводит к созданию временного значения делегата, а затем передается методу MethodThatTakesAdd. Это значение делегата зависит от сбора мусора в момент возвращения MethodThatTakesAdd, так как оно не сохраняет значение.

Во втором случае вы назначили делегату поле на внешнем экземпляре. Это будет обычно увеличивает время жизни делегата и, следовательно, уменьшает вероятность того, что это мусор, собранный во время вашего вызова pinvoke.

+0

Это звучит очень правдоподобно и объясняет, что я вижу! –

+0

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx объясняет это в разделе «Обратный P/Вызывать и делегировать время жизни» –

+0

@ JaredPar Я не понимаю.делает это 'AddDelegate DelegateInstance;' является экземпляром делегата ** **? это просто переменная типа 'AddDelegate'. логично, что экземпляр делегата ** был бы ** правой частью ** myDel + = new MYDEL (myMethod); '- или' myDel + = myMethod; '(короче) .... .Я ошибаюсь ? –

0

Хотя делегаты предоставляют синонимичную функциональность в C# в качестве указателей функций на C или C++, существуют значительные различия. Ключевым среди них является то, что делегат - это класс, а не указатель.

Короче говоря, литье делегата на указатель не даст вам ссылки на функцию или метод и поэтому не может использоваться для вызова метода по ссылке из неуправляемого кода.

4

Делегаты являются классами, которые являются вызываемыми, и имеют аналогичные поведение для указателей функций. Делегат внутренне сохраняет адрес вызываемой функции (то есть указатель функции), но также предоставляет другие функции, такие как многократное литье и сохранение списка вызовов; вы можете по существу вызвать много функций одной и той же сигнатуры с одним экземпляром делегата следующим образом.

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

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

Что касается вашей заметки об эквивалентности MethodThatTakesAdd(Add) и MethodThatTakesAdd(DelegateInstance), , если вы посмотрите на MSIL, что C# компилятор генерирует для линии MethodThatTakesAdd(Add), вы заметите, что компилятор создает делегат и оборачивать метод Add() для тебя.

Смежные вопросы