D:委托还是回调?

D: Delegates or callbacks?

我发现代表的概念对我来说很难。我真的不明白为什么我不能简单地将一个函数传递给另一个函数而需要将它包装到Delegate。我在文档中读到,在某些情况下我不知道它的名称,而委托是调用它的唯一方式。

但是现在我很难理解回调的概念。我试图找到更多信息,但我不明白它只是调用其他函数还是它是什么。

您能否展示 D 回调的示例并解释它们在哪些方面有帮助?

import vibe.d;

shared static this()
{
    auto settings = new HTTPServerSettings;
    settings.port = 8080;

    listenHTTP(settings, &handleRequest);
}

void handleRequest(HTTPServerRequest req,
                   HTTPServerResponse res)
{
    if (req.path == "/")
        res.writeBody("Hello, World!", "text/plain");
}

&handleRequest是回调吗?它是如何工作的,什么时候开始的?

所以在内存中一个函数只是一堆字节。就像数组一样,您可以获取指向它的指针。这是一个函数指针。它在 D 中具有 RETT function(ARGST) 类型。其中 RETT 是 return 类型,ARGST 是参数类型。当然,属性可以像任何函数声明一样应用。

现在委托是一个函数指针一个上下文指针。上下文指针可以是单个整数(参数)、调用框架(另一个函数内部的函数)或最后一个 class/struct.

委托与 RETT delegate(ARGST) 中的函数指针类型非常相似。它们不可互换,但您可以很容易地将函数指针转换为委托指针。

回调的概念是说,嘿,我知道你会知道 X,所以当发生这种情况时,请通过调用此 function/delegate 告诉我 X。

回答你关于 &handleRequest 的问题,是的,它是一个回调。

OP 中有几个问题。我将尝试回答以下两个问题:

问:您能否展示 D 回调的示例并解释它们在哪些方面有帮助?

A: 它们通常用于支持委托(例如 C#)作为事件处理程序的所有语言。 - 你给了一个委托,只要事件被触发就会被调用。不支持委托的语言为此目的使用 类 或回调函数。如何使用 FLTK 2.0 library: http://www.fltk.org/doc-2.0/html/group__example2.html 在 C++ 中使用回调的示例。委托是完美的选择,因为他们可以直接访问上下文。当您为此目的使用回调时,您必须传递所有要在回调中修改的对象...以提到的 FLTK link 为例 - 我们必须传递指向 [=11 的指针=] 对象到 window_callback 函数以便对其进行操作。 (FLTK之所以这样做是因为FLTK诞生时C++没有lambda,否则他们会用lambda代替回调)

示例 D 使用:http://dlang.org/phobos/std_signals.html

问:为什么我不能简单地将一个函数传递给另一个函数而需要将它包装到 Delegate 中?

A: 你不必包装委托 - 这取决于你想要完成什么......有时传递回调只会为你工作。您无法访问您可能想要调用回调的上下文,但委托可以。但是,您可以传递上下文(这就是一些 C/C++ 库所做的)。

我想您的问题在 D language reference

中有解释

引用 1:

A function pointer can point to a static nested function

引用 2:

A delegate can be set to a non-static nested function

查看该部分的最后一个示例,注意委托如何成为方法:

struct Foo
{
    int a = 7;
    int bar() { return a; }
}

int foo(int delegate() dg)
{
    return dg() + 1;
}

void test()
{
    int x = 27;
    int abc() { return x; }
    Foo f;
    int i;

    i = foo(&abc);   // i is set to 28
    i = foo(&f.bar); // i is set to 8
}

您可以将函数传递给其他函数以供稍后调用。

void test(){}
void receiver(void function() fn){
    // call it like a normal function with 'fn()'
    // or pass it around, save it, or ignore it
}

// main
receiver(&test); // 'test' will be available as 'fn' in 'receiver'

您需要在函数名称前加上 & 作为参数,以阐明您要传递函数指针。如果您不这样做,由于 UFCS(不带大括号的调用),它将改为调用该函数。还不是代表。

接收您的可调用函数的函数可以为所欲为。一个常见的例子是你的问题,一个网络服务回调。首先,您告诉框架在收到请求时应该做什么(通过在函数中定义操作并使该函数可用于框架),并在您的示例中输入一个带有 listenHTTP 的循环,它会在以下时间调用您的代码它收到一个请求。如果您想阅读更多有关此主题的信息:https://en.wikipedia.org/wiki/Event_(computing)#Event_handler

委托是附加了上下文信息的函数指针。假设您要添加处理程序以作用于当前上下文中可用的其他元素。就像将指示灯变为红色的按钮。示例:

class BuildGui {

    Indicator indicator;
    Button button;

    this(){
        ... init
        button.clickHandler({ // curly braces: implicit delegate in this case
            indicator.color = "red"; // notice access of BuildGui member
        });
        button.clickHandler(&otherClickHandler); // methods of instances can be delegates too
    }

    void otherClickHandler(){
        writeln("other click handler");
    }

}

在这个虚构的 Button class 中,所有点击处理程序都保存到列表中,并在点击时调用。

已经有很好的答案了。我只是想尝试做一个简单的总结。

简单地说:委托允许您将方法用作回调。

在 C 中,您通过将对象(多次命名为上下文)显式传递为 void* 并将其转换为(希望如此)正确的类型来执行相同操作:

void callback(void *context, ...) {
    /* Do operations with context, which is usually a struct */

    doSomething((struct DATA*)context, ...);
    doSomethingElse((struct DATA*)context, ...);
}

在 C++ 中,当您想要将方法用作回调时,您也可以这样做。您创建一个函数,将对象指针显式设为 void*,将其转换为(希望如此)正确的类型,然后调用方法:

void callback(void* object, ...) {
    ((MyObject*)object)->method(...);
}

Delegate 隐含地完成了这一切。