Go Variable Assignment with Interfaces and Dynamic Types

huangapple go评论88阅读模式
英文:

Go Variable Assignment with Interfaces and Dynamic Types

问题

我有一个脚本,根据用户输入从不同的数据源中获取数据,其中包括一个通用接口和每个数据源的类型。然后,每个数据源都有一个方法来获取该特定源的元数据。我在理解根据输入切换类型的 Go 语言惯用实现方面遇到了一些困难。

以下示例不能编译,但它是最能说明我想做的事情的版本:

type Post interface {
  GetMetadata() bool
}

type YouTubeVideo struct {
  ID            string
  Title         string
  ChannelID     string
  ChannelTitle  string
  PublishedAt   string
}

func (ig *YouTubeVideo) GetMetadata() bool {
  // ...
}

type InstagramPic struct {
  ID            string
  ShortCode     string
  Type          string
  Title         string
  PublishedAt   string
}

func (ig *InstagramPic) GetMetadata() bool {
  // ...
}

func main() {
  var thePost Post

  switch domain {
  case "youtube":
    thePost = new(YouTubeVideo)
    thePost.ID = pid
  case "instagram":
    thePost = new(InstagramPic)
    thePost.ShortCode = pid
  }

  thePost.GetMetadata()

  fmt.Println(thePost.title)
}

以上代码中,定义了一个 Post 接口,以及 YouTubeVideoInstagramPic 两个结构体,它们都实现了 GetMetadata 方法。在 main 函数中,根据输入的 domain 不同,创建相应的结构体实例,并调用 GetMetadata 方法。最后,打印出 thePosttitle 属性。

请注意,上述代码中的 fmt.Println(thePost.title) 应该改为 fmt.Println(thePost.Title),因为 Title 是结构体字段的名称。

英文:

I have a script that pulls from a different data source depending on the user input with a general interface and a type for each data source. Each data source then has a method to get the meta data for that particular source. I'm struggling a bit understanding the idomatic Go implementation to switch types depending on input.

This example does not compile, but it was the version that best illustrates what I want to do:

type Post interface {
  GetMetadata() bool
}

type YouTubeVideo struct {
  ID            string
  Title         string
  ChannelID     string
  ChannelTitle  string
  PublishedAt   string
}

func (ig *YouTubeVideo) GetMetadata() bool {
  // ...
}

type InstagramPic struct {
  ID            string
  ShortCode     string
  Type          string
  Title       string
  PublishedAt   string
}

func (ig *InstagramPic) GetMetadata() bool {
  // ...
}

func main() {
  var thePost Post

  switch domain {
  case "youtube":
	thePost = new(YouTubeVideo)
	thePost.ID = pid
  case "instagram":
	thePost = new(InstagramPic)
	thePost.ShortCode = pid
  }

  thePost.GetMetadata()

  fmt.Println(thePost.title)
}

答案1

得分: 1

根据细节,我认为你的结构总体上是正确的。但是需要更多的理解。

通过接口(比如Post),你只能访问接口定义的方法(在这种情况下是GetMetadata())。不能直接访问接口中存储的值(例如*YouTubeVideo*InstagramPic),除非使用**类型断言类型切换**。

因此,使用thePost.title是无法获取标题的。

获取Post的字段值

这里有两种选择(如果计算“类型断言”,则有三种选择):

1) 通过接口方法访问属性

type Post interface {
  GetMetadata() bool
  Title() string // 添加Title方法
}

func (ig *YouTubeVideo) Title() string {
  return ig.Title     
}

...

fmt.Println(thePost.Title())

2) 使用类型切换访问属性

switch v := thePost.(type) {
case *YouTubeVideo:
	fmt.Println(v.ChannelTitle)
case *InstagramPic:
	fmt.Println(v.PublishedAt)
}

选择1)适用于所有实现了Post接口的类型,也可以访问特定属性。选择2)允许你访问特定类型的字段,但需要为每种类型添加一个case。

设置Post的字段值

与获取时类似,不能直接设置接口值的字段。在你的情况下,你应该在将其存储到接口之前先设置所需的字段:

v := new(YouTubeVideo)
v.ID = pid
thePost = v // 将*YouTubeVideo存储到thePost中

或者更简洁一些:

thePost = &YouTubeVideo{ID: pid}

最后的注意事项

通过一些调整,你的结构应该可以使用接口和类型切换来工作。但是如何最好地组织它取决于你的具体情况,而我们对此了解的信息太少。

为了更好地理解如何使用接口,我建议阅读:Effective Go: Interfaces and Types

英文:

Depending on the details, I believe your structure is in general sound. But some more understanding is required.

With an interface, such as Post, you can only access the methods defined for the interface (GetMetadata() in this case). The value stored in the interface, eg. the *YouTubeVideo or *InstagramPic) cannot be accessed without a type assertion or type switch.

Therefore, it is not possible to get the title using thePost.title.

Getting a Post's field value

Here you have two alternatives (three if you count "type assertions"):

1) Add access to properties through Interface methods

type Post interface {
  GetMetadata() bool
  Title() string // Added Title method
}

func (ig *YouTubeVideo) Title() string {
  return ig.Title     
}

...

fmt.Println(thePost.Title())

2) Access the properties with a type switch

switch v := thePost.(type) {
case *YouTubeVideo:
	fmt.Println(v.ChannelTitle)
case *InstagramPic:
	fmt.Println(v.PublishedAt)
}

Alternative 1) is useful if all types that implements a Post also should give access to a certain property. 2) allows you to access fields specific for that type, but it requires then a case for each type.

Setting a Post's field value

Just like when getting, you can't directly set an Interface value's fields. In you case you should first set the desired fields before storing it in the interface:

v := new(YouTubeVideo)
v.ID = pid
thePost = v // Store the *YouTubeVideo in thePost

Or a bit shorter:

thePost = &YouTubeVideo{ID: pid}

Final note

With some tuning, your structure should work using interfaces and type switches. But exactly how to best structure it is dependent on your specific situation which we have too little information about.

To get a better understanding on how to use interfaces, I recommend reading: Effective Go: Interfaces and Types

huangapple
  • 本文由 发表于 2015年4月7日 09:39:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/29482343.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定