Go 中空接口的最佳实践?

Best practices for empty interfaces in Go?

我正在了解空接口。我发现虽然有很多关于空接口的含义以及它们如何工作的解释(包括 Whosebug 上的解释),但关于何时/为何使用它们、何时避免、注意事项以及注意事项的最佳实践的信息却很少选择使用它们的优缺点。

在 Go 聊天室中,我读过一些关于尽可能避免使用空接口但没有适当参数的讨论。其他人自豪地回应他们在代码设计中有零个空接口。

我最感兴趣的是用于库和框架(旨在被其他人重用/扩展)。

现在我正在阅读一个带有相关库的框架代码库,它在很多地方都充满了空接口。其中一些让我感到困惑,我想知道是否所有用法都是有道理的。就像框架提供 'user management' 和 AppConfigurationUserPreferences 之类的东西是空接口。这虽然在代码中更进一步(靠近数据库层),但用户的电子邮件在技术上被视为用户首选项。接口定义再具体一点不是更好吗?

空命名接口在 Go 中没有意义,因为与 C# 等其他语言不同,例如任何类型(C# 中的 class)如果与接口签名匹配,都可以转换为特定接口。所以在 Go 中“classes”(结构类型)不需要从接口继承(例如在类型定义时声明接口)。

所以你的问题的答案是:

Go 中空接口的最佳实践是不要在 Go 中定义命名空接口。

更新:感谢下面的评论,我改变了主意,我同意为了文档和在 IDE 中更快地导航。

In Go chatrooms I've read some talk about best to avoid using empty interfaces where possible, but without the proper arguments. Others proudly responding they had zero empty interfaces in their code design.

最大的问题是你输了所有的打字;例如,假设我有一个函数,我想对各种类型的数字进行操作,所以我写:

func AddOne(n interface{}) int64 {
    switch nn := n.(type) {
        case int:
            return nn + 1
        case int8:
            return nn + 1
        // ... etc...
    }
}

但是如果我将 0.42 (float64) 或字符串 "asd" 传递给此函数会怎样?如果该函数接受 int64 (func AddOne(n int64) int64),那么我们会在编译时 收到关于此的警告 ,但是对于 interface{} 你不会得到任何东西,因为一切都有效。

你能做的最好的事情是在 运行 时间处理这个问题,并且 panic() 或 return 一个错误;这显然比编译时错误要清楚得多。

这是迄今为止最大的缺点。


我必须特别查看那些 AppConfigurationUserPreferences 函数的详细信息,但是使用空接口有一些很好的理由;例如,当你想接受一些自定义结构,然后使用反射根据一些外部数据在结构上设置值时。这本质上就是 encoding/json 等软件包所做的,但它也常用于解析某些配置文件等。

听起来 AppConfigurationUserPreferences 可能适合这个,因为 library/framework 不知道您的应用程序配置需要什么设置,或者有什么用户偏好。但是就像我说的,没有细节就很难确定。

另一个用例是当你真的想接受各种各样的类型时;将参数传递给 SQL 查询是一个常见的例子,或者 fmt.Printf().


一般来说,避免使用 interface{} 可能是最好的选择,但如果您发现自己为此竭尽全力,那么最好只使用 interface{} 并忍受不用打字的情况。