英文:
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
接口,以及 YouTubeVideo
和 InstagramPic
两个结构体,它们都实现了 GetMetadata
方法。在 main
函数中,根据输入的 domain
不同,创建相应的结构体实例,并调用 GetMetadata
方法。最后,打印出 thePost
的 title
属性。
请注意,上述代码中的 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论