切片(从有限数量的接口中选择一个)

时间:2013-12-06 12:50:57

标签: interface go slice

如何让RemoveDead函数接受实现Liver的其他接口切片(甚至可能是结构指针切片),而对性能几乎没有影响?

在我看来,该函数必须以interface{}作为参数并进行运行时转换,但我不确定如何进行转换。我还猜测,x.(Liver)Liver(x)慢很多,因为后者是编译时转换。

复制粘贴函数并更改每个副本中的参数和返回类型是最佳解决方案吗?只需要三到四份副本,但它仍然会感觉像是一个非常笨拙的解决方案。

type Updater interface {
    Liver
    Update() bool
}

type Liver interface {
    Alive() bool
}

func RemoveDead(all []Updater) []Updater {
    for i := len(all) - 1; i >= 0; i-- {
        if Liver(all[i]).Alive() == false {
            all[i] = all[len(all)-1]
            all = all[:len(all)-1]
        }
    }
    return all
}

2 个答案:

答案 0 :(得分:1)

正如您所提到的,[]Updater类型的切片无法通过简单的类型断言转换为[]Liver;它们的类型不是接口,而是接口片。出于同样的原因,无法将[]Updater传递给想要[]interface{}作为参数的函数。

但是,您可以使用reflect包执行所需操作。反思很有用,但会以性能为代价。如果您认为成本较高,那么您可能必须使用复制粘贴解决方案。

下面的代码肯定可以改进,但它显示了如何用反射来解决问题,并且在制作基准时它可能很有用。目前它认为任何甚至U值都是Alive:

package main

import (
    "fmt"
    "reflect"
)

type Updater interface {
    Alive() bool
    Update() bool
}

type Liver interface {
    Alive() bool
}

type U int
func (u U) Alive() bool  { return u % 2 == 0 }

func RemoveDead(all interface{}) interface{} {
    v := reflect.ValueOf(all)
    if v.Kind() != reflect.Slice {
        panic("RemoveDead requires a slice")
    }
    for i := v.Len() - 1; i >= 0; i-- {
        l := v.Index(i)
        if l.Interface().(Liver).Alive() == false {
            l.Set(v.Index(v.Len()-1))
            v = v.Slice(0, v.Len()-1)
        }
    }
    return v.Interface()
}

func main() {
    u := []U{1,4,7,2,12}
    fmt.Println("Before: ", u)
    u  = RemoveDead(u).([]U)
    fmt.Println("After: ", u)
}

输出:

Before:  [1 4 7 2 12]
After:  [2 4 12]

Playground

答案 1 :(得分:0)

您可以定义第三个界面:

type UpdaterLiver interface {
    Updater
    Liver
}

然后将RemoveDead的定义更改为

func RemoveDead(all []UpdaterLiver) []UpdaterLiver