【问题标题】:Best practices for empty interfaces in Go?Go 中空接口的最佳实践?
【发布时间】:2021-01-14 10:27:12
【问题描述】:

我正在学习空接口。我发现虽然有很多解释——也在 Stackoverflow 上——关于空接口的含义以及它们如何工作,但关于何时/为什么使用它们、何时避免、注意事项是什么以及关于最佳实践的信息很少。选择使用它们的利弊。

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

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

现在我正在阅读一个带有相关库的框架代码库,它在许多地方都充满了 emtpy 接口。其中一些让我感到困惑,我想知道是否所有的用法都是合理的。就像框架提供“用户管理”一样,AppConfigurationUserPreferences 之类的东西都是空接口。在代码中(靠近 db 层),用户的电子邮件在技术上被视为用户偏好。接口定义更具体一些不是更好吗?

【问题讨论】:

标签: go interface


【解决方案1】:

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

所以你的问题的答案:

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

更新:感谢下面的 cmets,我改变了主意,我同意使用空命名接口的用例有限,以便在 IDE 中进行文档编制和更快的导航。

【讨论】:

  • “空命名接口在 Go 中没有意义”——除非它确实有意义。
  • 另外,Go 中没有类,也没有继承。目前尚不清楚您是否尝试在 C# 或 Go 的上下文中使用这些术语。
  • 出于同样的原因,您可以使用任何其他基本类型的命名类型:文档。
  • 如果你的试金石是标准库,here is a famous example。还有很多其他的。即1, 2, ...
  • 感谢您的链接,非常感谢。虽然链接 1 和 2 会导致测试代码,如果您想根据它来测试行为,那么显然可以使用空的命名接口。关于“著名的例子”,它是一个很好且有效的例子,为了论证,在研究了代码之后,我同意为了文档而使用它可能是有意义的。我也认为它在某些用例中对 IDE 很有用。再次感谢您的教育。
【解决方案2】:

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

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

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() 或者返回一个错误;这显然比编译时错误要清楚得多。

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


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

听起来AppConfigurationUserPreferences 可能适合这个,因为库/框架不知道您的应用配置需要哪些设置,或者有哪些用户偏好。但就像我说的,没有细节很难确定。

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


一般来说,避免interface{} 可能是最好的,但如果你发现自己为此弯腰弯腰,那么最好只使用interface{} 并避免打字。

【讨论】:

  • 我认为问题是关于命名为空的接口,例如type UserPreferences interface{}
  • 谢谢@martin-tournoij!对于 libs + 框架,我只是指旨在被其他人重用和扩展的代码。有一些命名的空接口,还有像func (a *App) DefaultUserPreferences() interface{} 这样返回nil 的东西。然而,用户首选项应包括电子邮件,这是数据库中必需的。
  • 命名/类型化空接口的优点之一是您可以将方法附加到它们@ArnoldSchrijver;一般来说,在没有interface{} 的情况下编写一些通用的用户身份验证库需要一些努力(尽管这也不是不可能的,因为我已经这样做了)。但就像我说的,很难确定;可能有一些特性或考虑因素使界面难以使用。
  • @MartinTournoij 据我所知,我们不能将方法“附加”到命名接口,只能附加到“结构类型”。如果我错了,请纠正我?
  • 您可以将方法附加到任何类型@AlexanderTrakhimenok(type x string,我已经这样做了很多次),但实际上我现在不确定接口类型。看起来你不能:invalid receiver type named (named is an interface type) 所以我想我错了?
猜你喜欢
  • 2019-04-08
  • 2014-02-28
  • 2013-02-16
  • 1970-01-01
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多