【问题标题】:Slice of a specific interface in GoGo 中特定接口的切片
【发布时间】:2017-04-12 10:06:09
【问题描述】:

我想创建一个使用接口切片的函数。

例如,我有一个接口ider,它包装了一个ID() string 函数。然后我有一个类型(Field),它实现了ider 接口。现在我有不同的对象,它们有 ider 的切片作为参数。

在我的函数中,我期望得到ider ([]ider) 的一部分。该函数应该被不同的类型使用,它们正在实现ider

这很难描述。所以这里是一个完整的例子,输出如下错误:

不能在 inSlice 的参数中使用 myObj.Fields (type []*Field) 作为 type []ider

type ider interface {
    ID() string
}

type Field struct {
    Name string
}

// Implements ider interface
func (f *Field) ID() string {
    return f.Name
}

type MyObject struct {
    Fields []*Field
}

// uses slice of ider
func inSlice(idSlice []ider, s string) bool {
    for _, v := range idSlice {
        if s == v.ID() {
            return true
        }
    }
    return false
}

func main() {
    fields := []*Field{
        &Field{"Field1"},
    }
    myObj := MyObject{
        Fields: fields,
    }
    fmt.Println(inSlice(myObj.Fields, "Field1"))
}

https://play.golang.org/p/p8PPH51vxP

我已经搜索过答案,但我只是找到了针对空接口而不是针对特定接口的解决方案。

【问题讨论】:

标签: go


【解决方案1】:

大家可以在https://golang.org/ref/spec#Calls阅读

除一种特殊情况外,参数必须是可分配给 F 的参数类型的单值表达式,并在调用函数之前进行评估。

所以在上面的代码中myObj.Fields 的类型是[]*Field,它需要分配给[]ider 才能编译代码。让我们检查一下是不是这样。可以阅读https://golang.org/ref/spec#Assignability

在以下任何一种情况下,值 x 都可以分配给 T 类型的变量(“x 可分配给 T”):

  • x 的类型与 T 相同。
  • x 的类型 V 和 T 具有相同的底层类型,并且 V 或 T 中的至少一个不是命名类型。
  • T 是一个接口类型,x 实现了 T。
  • x为双向通道值,T为通道类型,x的类型V和T具有相同的元素类型,V或T中至少有一个不是命名类型。
  • x 是预先声明的标识符 nil,T 是指针、函数、切片、映射、通道或接口类型。
  • x 是一个无类型的常量,可以用 T 类型的值表示。
  1. []*Field[]ider 是否相同? https://golang.org/ref/spec#Type_identity告诉我们

    如果两个切片类型具有相同的元素类型,则它们是相同的。

    那么*Fieldider 是否相同? 以上来源告诉我们

    命名类型和未命名类型总是不同的。

    所以没有也没有,因为*Field 未命名,ider 已命名。

  2. []*Field 的底层类型是[]*Field[]ider 的底层类型是[]ider,它们并不相同,正如我们在 1 中检查的那样。所以这也不适用。阅读此处https://golang.org/ref/spec#Types

  3. 不适用,因为 []ider 不是接口类型,而是切片类型。阅读此处https://golang.org/ref/spec#Slice_types

  4. 也不适用,因为没有使用频道

  5. 也不适用,因为没有使用 nil

  6. 也不适用,因为没有使用常量。

总结一下:[]*Field 类型的值不能分配给[]ider,因此我们不能在参数位置使用[]*Field 类型的表达式 参数类型为[]ider的函数调用。

【讨论】:

  • 在 go 中,您通常会执行 var asIder []ider; for _, v := range myObj.Fields { asIder = append(asIder, v) }; inSlice(asIder, "Field1") 之类的操作,或者将转换封装在一个函数中。
猜你喜欢
  • 1970-01-01
  • 2016-07-17
  • 2016-10-13
  • 2017-10-03
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 2013-04-21
相关资源
最近更新 更多