为什么我不能在go中将一个嵌入式结构体赋值给父结构体?

sq1bmfud  于 2023-02-01  发布在  Go
关注(0)|答案(3)|浏览(264)

我有下面的代码试图将嵌入结构分配给其父结构。有两组结构:Guider是父结构体,DataBlock从它扩展而来。方法func Put(x Guider)接受一个类型为Guider的参数。当我传递一个DataBlock变量时,它工作。
但是,另一种情况是Mockzerolog.Event扩展而来,但它未能在方法Test(e zerolog.Event)上传递参数
出现以下错误:
无法将m(类型为Mock的变量)用作类型zerolog。Test参数中的事件
为什么这两种情况不同?我怎样才能使它们都起作用?

package main

import (
    "fmt"

    "github.com/rs/zerolog"
)

type Guider interface {
    Guid() string
}
type FSEntity struct {
    guid string
}

func (e FSEntity) Guid() string {
    return e.guid
}

func Put(x Guider) {
    fmt.Printf("%+v\n", x)

}

type Mock struct {
    zerolog.Event
}

func Test(e zerolog.Event) {

}

//Child struct:

type DataBlock struct {
    FSEntity
    data []byte
}

func main() {
    myVar := DataBlock{}
    myVar.guid = "test"
    myVar.data = []byte("moar test")
    Put(myVar) // it works

    m := Mock{}
    Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}
b1zrtrql

b1zrtrql1#

首先,有几个定义:
Polymorphism
多态性是为不同类型的实体提供单个接口,或者使用单个符号表示多个不同类型。
Subtyping
子类型化(也称为子类型多态性或包含多态性)是类型多态性的一种形式,其中子类型是通过某种可替换性概念与另一数据类型(超类型)相关的数据类型,这意味着被编写为对超类型的元素进行操作的程序元素(通常为子例程或函数)也可以对子类型的元素进行操作
Inheritance
在面向对象编程中,继承是一种将一个对象或类建立在另一个对象(基于原型的继承)或类(基于类的继承)之上,并保持相似实现的机制。
Object composition
对象组合和对象聚合是将对象或数据类型组合成更复杂对象或数据类型的密切相关的方法。
Golang遵循composition over inheritance principle,例如,它不支持继承。
模拟从zerolog.事件扩展
你的意思是Mock包含了zerolog.Event结构。
Golang实现多态的方式是interface,所有实现某个接口的类型都可以在它的位置上使用,这就是你在使用Guider时看到的。
但是,它不适用于简单的结构体。zerolog.EventMock内部的结构体。
因此,通常情况下,Test函数应该接受某个接口作为参数,模拟事件和真实的事件都应该实现这个接口。然而,看起来zerolog没有为Event提供接口。因此,您应该访问结构体的Event字段。Example

jaql4c8m

jaql4c8m2#

Put(myVar)是法律的的,因为myVar是一个DataBlock,它 * 包含 *(不是继承也不是实现)一个FSEntity,而FSEntity又 * 实现 * Guider接口。
由于Put接受Guider,所以对myVar的引用是兼容的,因为它包含匿名的FSEntity字段,该字段实现GuiderFSEntityGuider的实现(实际上)被提升到包含结构(提供了一种委托接口的方法)。这仅在包含字段是匿名的情况下发生。
但是在Test(m)的情况下,函数接受zerolog.Event,它是struct类型,而不是interface。因此,不可能有“委托”。Test()必须传递zerolog.Event,在这个场景中,这要求您使用匿名字段的类型名称:

Type(m.Event)

一些奖金信息:
如果DataBlock包含 * 两个 * 匿名字段,这两个匿名字段都实现了Guider,则不能发生隐式委托/提升;golang不知道应该将包含的哪个实现委托给/elevated(如果有的话),在这种情况下,您必须再次使用希望传递给Put()函数的字段的名称:

// given... 
   type Foo string

   func (f Foo) Guid() string {
      return string(f)
   }

   // and...
   type DataBlock struct {
      FSEntity
      Foo
      data []byte
   }

   // then...
   Put(myVar)   // is now illegal

   // and must instead use either/or:
   Put(myVar.FSEntity)
   Put(myVar.Foo)

无论是隐式的还是显式的,关键的区别在于传递给Put()的是DataBlockmyVar)的一个 * 字段 ,而不是*myVar本身。
如果要使用Guider接口将DataBlock传递到Put(),则DataBlock必须自己实现Guider接口。

xyhw6mcr

xyhw6mcr3#

对此持保留态度,因为我对zerolog包不熟悉。
你的Guider是一个接口,只要Guid()方法满足,它可以有任何底层类型。我假设这是通过包含FSEntityDataBlock发生的,FSEntity本身实现Guid()方法,因此满足MIGHT接口。
另一方面,我不知道应该实现什么方法来满足zerolog.Event,或者它是一个接口,还是一个结构体。如果它是一个接口,你可能需要实现它所需的方法,以便能够将DataBlock用作zerolog.Event类型。你可能想要/需要深入研究这个方向,以获得一些非常具体的答案。

相关问题