20

filepath.Walk函数接受函数回调。这是没有上下文指针的直接函数。当然,一个主要的用例Walk是遍历一个目录并基于它采取一些行动,参考更广泛的上下文(例如,将每个文件输入到一个表中)。

如果我在 C# 中编写此代码,我将使用一个对象(具有可以指向上下文中的对象的字段)作为其上的回调(具有给定的回调方法),以便该对象可以封装Walk从中调用的上下文。

(编辑:用户“ usr ”建议关闭方法也出现在 C# 中)

如果我在 C 中编写这个,我会要求一个函数和一个上下文指针,void *这样函数就有一个上下文指针,它可以传递给Walk函数并将其传递给回调函数。

但是 Go 只有函数参数,没有明显的上下文指针参数。

(如果我设计了这个函数,我会将对象作为回调而不是函数,符合接口FileWalkerCallback或其他,并callback(...)在该接口上放置一个方法。然后消费者可以在传递之前将任何上下文附加到对象到Walk。)

我能想到的唯一方法是在回调函数中捕获外部函数的闭包。这是我使用它的方式:

func ScanAllFiles(location string, myStorageThing *StorageThing) (err error) {
    numScanned = 0

    // Wrap this up in this function's closure to capture the `corpus` binding.
    var scan = func(path string, fileInfo os.FileInfo, inpErr error) (err error) {
        numScanned ++

        myStorageThing.DoSomething(path)
    }

    fmt.Println("Scan All")

    err = filepath.Walk(location, scan)

    fmt.Println("Total scanned", numScanned)

    return
}

在此示例中,我创建了回调函数,因此它的闭包包含变量numScannedmyStorageThing.

这对我来说感觉不对。我认为这感觉很奇怪是对的,还是我只是习惯了写 Go?如何filepath.Walk以回调引用更广泛的上下文的方式使用该方法?

4

2 回答 2

16

你这样做是对的。您可以考虑两种小变化。一种是您可以用下划线替换未使用参数的名称。因此,在您仅使用路径的示例中,签名可以读取

func(path string, _ os.FileInfo, _ error) error

它节省了一点打字,清理了一点代码,并清楚地表明您没有使用该参数。此外,特别是对于小型函数,通常跳过将函数文字分配给变量,而直接将其用作参数。您的代码最终会阅读,

err = filepath.Walk(location, func(path string, _ os.FileInfo, _ error) error {
    numScanned ++

    myStorageThing.DoSomething(path)
})

这稍微清理了范围,清楚地表明您只使用了一次闭包。

于 2012-07-05T16:32:04.723 回答
0

作为一名 C# 程序员,我可以说这正是 .NET 中这样一个 API 的用途。鼓励您使用闭包,不鼓励创建带有字段的显式类,因为这只会浪费您的时间。

由于 Go 支持闭包,我会说这是使用此 API 的正确方法。我看不出有什么问题。

于 2012-07-04T22:39:15.377 回答