无法在使用 Go 的函数中覆盖列表类型接口 {}

Lov*_*arn 1 interface go slice

无法在使用 Go 的函数中覆盖列表类型接口 {}。

  • 对我来说很重要然后我在一个函数中执行。

怎么修?

package main

import (
    "fmt"
)

type MyBoxItem struct {
    Name string
}

type MyBox struct {
    Items []MyBoxItem
}

func (box *MyBox) AddItem(item MyBoxItem) []MyBoxItem {
    box.Items = append(box.Items, item)
    return box.Items
}

func PrintCustomArray(list interface{}) interface{}  {
    //items := reflect.ValueOf(list)
    for _, v := range list {
        fmt.Println(v.Key,v.Value)
    }
    return 0
}
func main() {

    items := []MyBoxItem{}
    item := MyBoxItem{Name: "Test Item 1"}
    box := MyBox{items}
    box.AddItem(item)
    fmt.Println((box.Items))
    PrintCustomArray(box.Items)
}
Run Code Online (Sandbox Code Playgroud)

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

错误 : cannot range over list (type interface {})

怎么修?

Eli*_*gem 5

笔记

下面的答案概括地描述了两种可能的方法:使用接口和使用特定类型。为了完整起见,提到了专注于接口的方法。恕我直言,您提出的案例不是接口的可行用例。

下面,您将找到使用这两种技术的游乐场示例的链接。任何人都应该清楚,对于这种特定情况,接口方法太麻烦了。


除了您似乎不太熟悉循环在 go 中的工作方式(例如v.Keyv.Value它们是不存在的字段)这一事实之外,我将尝试回答您的问题。

果然,您正在将一个列表传递给您的函数,但它被作为一种interface{}类型处理。这意味着您的函数本质上接受任何值作为参数。你不能简单地迭代它们。

您可以做的是使用类型断言将参数转换为切片,然后使用另一个断言将其用作另一个特定接口:

type Item interface{
    key() string
    val() string
}

func (i MyBoxItem) key() string {
    return i.Key
}

func (i MyBoxItem) val() string {
    return i.Value
}

func PrintCustomArray(list interface{}) error  {
    listSlice, ok := list.([]interface{})
    if !ok {
        return fmt.Errorf("Argument is not a slice")
    }
    for _, v := range listSlice {
        item, ok := v.(Item)
        if !ok {
            return fmt.Errorf("element in slice does not implement the Item interface")
        }
        fmt.Println(item.key(), item.val())
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

但老实说,这样的函数只有在切片作为参数传递时才有效。因此,在那里使用第一种类型的断言毫无意义。至少,将函数改成这样更有意义:

func PrintCustomArray(list []interface{})
Run Code Online (Sandbox Code Playgroud)

然后,因为我们不期待这样的数组,而是一个切片,所以名称应该更改为PrintCustomSlice.
最后,因为我们对切片中的每个值使用相同的类型断言,我们不妨进一步更改函数:

// at this point, we'll always return 0, which is pointless
// just don't return anything
func PrintCustomSlice(list []Item) {
    for _, v := range list {
        fmt.Println(v.key(), v.val())
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样的函数的优点是它仍然可以处理多种类型(你所要做的就是实现接口)。您不需要任何类型的昂贵操作(如反射)或类型断言。

类型断言非常有用,但在这种情况下,它们仅用于隐藏否则会导致编译时错误的问题。Go 的interface{}类型是一个非常有用的东西,但您似乎正在使用它来绕过类型系统。如果这就是您想要实现的目标,为什么首先要使用类型化语言?

一些结束语/评论:如果您的函数仅用于迭代特定的“事物”,则根本不需要接口,只需在第一个中指定您期望传递给函数的类型地方。在这种情况下,这将是:

func PrintCustomSlice(list []MyBoxItem) {
    for _, v := range list {
        fmt.Println(v.Key, v.Value)
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到的另一件事是您似乎正在导出所有内容(所有函数、类型和字段都以大写字母开头)。这在 go 中被认为是不好的形式。只导出需要公开的内容。在main包中,这通常意味着您几乎不会导出任何内容。

最后,正如我在开头提到的:您似乎还没有牢牢掌握基础知识。我强烈建议您参加互动导览。它很好地涵盖了基础知识,但以适当的速度向您展示了该语言的功能。不需要很长时间,值得花几个小时来完成


游乐场演示