回调函数是否与C#(。NET)中的事件等效。
我对回调函数的理解是,它是一个通过对该函数的引用调用的函数。
示例代码将是:
void cbfunc()
{
printf("called");
}
int main ()
{
void (*callback)(void);
callback=(void *)cbfunc;
callback();
return 0;
}
现在我不明白的是,从DLL到客户端的通知是如何完全使用的。
假设我想在我的DLL方法2()上接收数据时执行/执行一些method1()。
与.NET中的事件进行任何比较都会有很好的帮助。
答案 0 :(得分:1)
您将指针传递给第三方例程(不一定是DLL),并且在需要通知时被克隆指针“回调”。
它类似于.net事件,因为后者也是一种回调。
BTW,在C ++中,DLL比在.NET中更不受欢迎。这是因为不可能共享静态变量(因此也就是单例),这个问题也称为缺乏动态链接(在UNIX系统中,这是通过共享对象解决的,这是动态加载库的完全不同的概念)。静态库提供了更好的代码重用策略。答案 1 :(得分:1)
回调函数与C#中的代理类似。
例如,Win32 API提供了一个定时服务,可以通过调用SetTimer来访问。 SetTimer由系统DLL导出,但其机制与在用户dll中使用的机制完全相同。在您的代码中,您可以通过执行以下操作来访问计时器:
void
CALLBACK
MyTimerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// do something
}
...
TIMERPROC fn = &MyTimerCallback;
int delay = 500;
SetTimer(NULL,0,delay,fn);
调用SetTimer并传入回调函数,允许操作系统在每次计时器滴答时回调函数。当然,这里没有多播功能,特别是在SetTimer的情况下,回调函数必须是C函数或静态类方法。没有与该函数关联的类或对象实例。
类似的模式可以在.NET中完成 - 我确信.NET有自己的Timer范例,但是暂时我们可以假装它实现了一个带有TimerDelegate的SetTimer函数。
在用户代码中,在对象中,您可以将MyTimerProc定义为具有与委托匹配的签名的函数。并像这样调用它
TimerDelegate d = new TimerDelegate(myObject.MyTimerProc);
SetTimer(0,0,delay,d);
或者,如果“timers”是与TimerDelegate匹配的事件,那么等效的C#代码将类似于:
timers += new TimerDelegate(myObject.MyTimerProc);
注意:我的C#非常生疏,所以不要将这些代码示例作为最佳实践的任何示例,甚至是工作代码:P
在定义自己的回调函数时,最好定义回调函数以获取void *“context”参数,因为这样可以让C ++程序员存储它们的“this”指针并检索它。
// the first parameter to the callback fn is a void* user supplied context parameter
typedef void (CALLBACK* MyCallbackFn)(void* context, int etc);
// and, the dll export function always takes a function pointer, context parameter pair.
DLL_EXPORT void SomeExportedFn(MyCallbackFn, void* context);
答案 2 :(得分:1)
回调和接口类是管理代码边界的好方法。它们有助于在代码中创建正式的边界和/或层,而不是将所有内容混为一谈。在处理大型软件解决方案时,这是必要的。
下面是如何使用回调和接口类的示例。在库/ DLL代码中,唯一应该暴露给主可执行文件的是myddl_interface类和函数getMyDllInterface()。使用这样的接口类完全隐藏了主可执行文件的实现细节。接口类还允许主可执行文件向其注册一个稍后将执行的函数(即回调)。
// Begin library/dll Public Interface used by an executable
class mydll_interface {
public:
typedef void (*callback_func_t)();
public:
virtual void do_something() = 0;
virtual void registerFunction( callback_func_t ) = 0;
};
static mydll_interface* getMyDllInterface();
// End library/dll Public Interface used by an executable
// Begin library/dll Private implementation
class mydll_implementation : public mydll_interface {
public:
void do_something() {
printf("Hello World\n");
_callback_func();
}
void registerFunction( callback_func_t c) {
_callback_func = c;
}
private:
callback_func_t _callback_func;
};
static mydll_interface* getMyDllInterface() {
return new mydll_implementation();
};
// End library/dll Private implementation
// Begin main executable code
void myMainAppFunc() {
printf("hello World Again\n");
}
int main() {
mydll_interface* iface = getMyDllInterface();
iface->registerFunction(&myMainAppFunc);
iface->do_something();
};
// End main executable code
答案 3 :(得分:-1)
一个值得注意的例子是qSort(),它需要一个回调函数来比较2个项目。 回调函数可以在运行时决定某些行为。
如果某些行为应该由客户端在运行时动态决定,那么我将要求客户端提供回调函数。