调用Go函数,该函数接受具有一片结构B的接口片A(B实现A) [英] Call Go function that accepts a slice of interface A with a slice of struct B (B implements A)

查看:84
本文介绍了调用Go函数,该函数接受具有一片结构B的接口片A(B实现A)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  type Statement接口{
Say()string
}

类型引用struct {
引用字符串
}

func(p引号)Say()string {
return p.quote

$ b $ func Replay(conversation [] Statement){
for _,statement:= range conversation {
fmt.Println(statement.Say())






$ b

我认为我有一个相当好的理解为什么一个函数接受 []语句类型的参数,不能用 []引用调用;即使 Quote 实现语句 []引用不会实现 []语句 []语句甚至不是一个接口。它的类型是 Statement of 。虽然Go隐式地从一个类型转换为一个接口类型,但它并没有从类型的片段的片段隐式转换为一个接口片段 B code $。

我们可以明确地将引号转换为语句:

  conversation:= [] Quote {
Quote {好人Eddie:来吧,投降!},
Quote {Pink先生:呃,我,不要小费。},
Quote {好人埃迪:你不小费?},
Quote {粉红先生:不,我不相信。 },
Quote {好人Eddie:你不相信小费?},
}

//这不起作用
//重播(对话)

//从引号创建语句
语句:= make([] Statement,len(对话))
for i,quote:= range conversation {
语句[i] =报价
}

重播(语句)

现在说Replay是一个图书馆的一部分,它想要使用Repla年。只要这些对象实现了Statement接口,它就允许您使用任何切片对象调用Replay。要做到这一点,它有以下转换方法:

$ p $ func ConvertToStatements(它的接口{})([]语句,错误){
itsValue:= reflect.ValueOf(它)
itsKind:= itsValue.Kind()
如果它的Kind!= reflect.Array&& itsKind!= reflect.Slice {
return nil,fmt.Errorf(Expected items to be a Array or a Slice,got%s,itsKind)
}
itsLength:= itsValue。 Len()
items:= make([] Statement,itsLength)
for i:= 0;我< itsLength; i ++ {
itsItem:= itsValue.Index(i)
if item,ok:= itsItem.Interface()。(Statement); OK {
items [i] = item
} else {
return nil,fmt.Errorf(item#%d does not implement Statement接口:%s,i,itsItem)
}
}
退货项目,无
}



<重播看起来像这样:

  func Replay(它的接口{}){
对话:= ConvertToStatements )
for _,statement:=范围对话{
fmt.Println(statement.Say())
}
}

我们现在可以直接用引号调用Replay:

 重播(对话)

最后,我的问题是否有更简单的方法让Replay接受切片任何类型的A,只要A实现了Statement接口?

解决方案

内存中布局的 [] Quote 切片与 [] Statement 切片不同,所以这是不可能的。



[] Quote slice的支持数组将包含顺序的 Quote 结构,而 [] Statement 切片的后备数组由接口变量组成。除了持有 Quote 结构(或其他任何类型实现接口)外,接口变量还存储指向所包含值的类型信息的指针。这需要确定如何分派 Say 方法调用。



不同的数据布局意味着您可以' t交换两种切片类型,甚至不会通过不安全的转换:如果您有一种类型,需要另一种类型,则需要手动转换它们。


I have the following types:

type Statement interface {
    Say() string
}

type Quote struct {
    quote string
}

func (p Quote) Say() string {
    return p.quote
}

func Replay(conversation []Statement) {
    for _, statement := range conversation {
        fmt.Println(statement.Say())
    }
}

I think I have a fairly good grasp of why a function that accepts a parameter of type []Statement, cannot be called with []Quote; even though Quote implements Statement, []Quote does not implement []Statement. []Statement is not even an interface. It has the type slice of Statement. While Go implicitly converts from a type to an interface type, it does no implicit conversion from a slice of type A to a slice of interface B.

We can convert the quotes to statements explicitly:

conversation := []Quote{
    Quote{"Nice Guy Eddie: C'mon, throw in a buck!"},
    Quote{"Mr. Pink: Uh-uh, I don't tip."},
    Quote{"Nice Guy Eddie: You don't tip?"},
    Quote{"Mr. Pink: Nah, I don't believe in it."},
    Quote{"Nice Guy Eddie: You don't believe in tipping?"},
}

// This doesn't work
// Replay(conversation)

// Create statements from quotes
statements := make([]Statement, len(conversation))
for i, quote := range conversation {
    statements[i] = quote
}

Replay(statements)

Now say that Replay is part of a library that wants to go out of its way in how easy it's to use Replay. It allows you to call Replay with any slice of objects as long as those objects implement the Statement interface. To do so it has the following conversion method:

func ConvertToStatements(its interface{}) ([]Statement, error) {
    itsValue := reflect.ValueOf(its)
    itsKind := itsValue.Kind()
    if itsKind != reflect.Array && itsKind != reflect.Slice {
        return nil, fmt.Errorf("Expected items to be an Array or a Slice, got %s", itsKind)
    }
    itsLength := itsValue.Len()
    items := make([]Statement, itsLength)
    for i := 0; i < itsLength; i++ {
        itsItem := itsValue.Index(i)
        if item, ok := itsItem.Interface().(Statement); ok {
            items[i] = item
        } else {
            return nil, fmt.Errorf("item #%d does not implement the Statement interface: %s", i, itsItem)
        }
    }
    return items, nil
}

Replay looks like this:

func Replay(its interface{}) {
    conversation := ConvertToStatements(its)
    for _, statement := range conversation {
        fmt.Println(statement.Say())
    }
}

We can now call Replay with quotes directly:

Replay(conversation)

Finally, my question: Is there a simpler way to allow Replay to accept a slice of any type A, as long as A implements the Statement interface?

解决方案

The in-memory layout of a []Quote slice is different to a []Statement slice, so this is not possible.

The backing array of the []Quote slice will consist of the sequential Quote structs, while the []Statement slice's backing array consists of interface variables. As well as holding the Quote struct (or whatever other type implements the interface), the interface variable also stores a pointer to type information for the contained value. This is needed to determine how to dispatch the Say method call.

The different data layout means that you can't interchange the two slice types, not even through unsafe casts: if you have one type and need the other you'll need to manually convert between them.

这篇关于调用Go函数,该函数接受具有一片结构B的接口片A(B实现A)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆